Changeset 1836
- Timestamp:
- Sep 20, 2004, 1:16:10 PM (22 years ago)
- Location:
- trunk/psLib/src
- Files:
-
- 2 edited
-
dataManip/psMinimize.c (modified) (9 diffs)
-
math/psMinimize.c (modified) (9 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psLib/src/dataManip/psMinimize.c
r1831 r1836 9 9 * @author George Gusciora, MHPCC 10 10 * 11 * @version $Revision: 1.4 0$ $Name: not supported by cvs2svn $12 * @date $Date: 2004-09- 18 01:50:45$11 * @version $Revision: 1.41 $ $Name: not supported by cvs2svn $ 12 * @date $Date: 2004-09-20 23:16:10 $ 13 13 * 14 14 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii … … 94 94 /*****************************************************************************/ 95 95 96 typedef struct97 {98 size_t n; // Number of data points points in domain.99 int paramCount; // Number of non-masked parameters.100 psVector* restrict initialGuess;101 const psImage* restrict domain;102 const psVector* restrict data;103 const psVector* restrict errors;104 const psVector* restrict paramMask;105 psMinimizeFunction evalModel;106 psMinimizeFunctionDeriv d_evalModel;107 }108 psMinChi2Data;109 110 typedef struct111 {112 int paramCount; // Number of non-masked parameters.113 psVector* restrict initialGuess;114 const psVector* restrict coord;115 const psVector* restrict paramMask;116 psMinimizeFunction evalModel;117 psMinimizeFunctionDeriv d_evalModel;118 }119 psMinimizeData;120 121 96 /*****************************************************************************/ 122 97 /* GLOBAL VARIABLES */ … … 137 112 /* FUNCTION IMPLEMENTATION - LOCAL */ 138 113 /*****************************************************************************/ 139 140 /******************************************************************************141 p_psMinFunc(*params, *funcData): We use the GSL procedure142 gsl_multimin_fdfminimizer_iterate() to minimize an arbitary function supplied143 by the user. That GSL procedure requires the function to be minimized to be144 in a different format than the psLib format. The purpose of this procedure145 is to serve as a GSL-format wrapper for the user-supplied procedure which is146 to be minimized.147 148 params: The parameters of the function to be minimized. These will be149 varied by GSL in order to minimize the function.150 151 funcData: a private psLib struct which contains the data point to be152 minimized, the function and derivative function pointers, an initial153 guess at the parameters, an option parameter mask, etc.154 *****************************************************************************/155 double p_psMinFunc(const gsl_vector * params, void *funcData)156 {157 int i; // Loop index variable.158 int j; // Loop index variable.159 float tmpf; // Temporary floating point variable.160 const psVector* restrict coord = ((psMinimizeData* ) funcData)->coord;161 const psVector* restrict mask = ((psMinimizeData* ) funcData)->paramMask;162 psVector* restrict initialGuess = ((psMinimizeData* ) funcData)->initialGuess;163 psMinimizeFunction evalModel = ((psMinimizeData* ) funcData)->evalModel;164 psVector* inputParameterList = NULL;165 166 // The GSL routines will call this function with the masked parameters167 // removed. However, the user-supplied function (to be modified) does not168 // have those parameters removed. Here will create a new parameter list169 // with the masked parameters added (we expand initialGuess).170 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);171 if (mask != NULL) {172 j = 0;173 for (i = 0; i < mask->n; i++) {174 if (mask->data.U8[i] != 0) {175 inputParameterList->data.F32[i] = initialGuess->data.F32[i];176 } else {177 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);178 }179 }180 } else {181 for (i = 0; i < initialGuess->n; i++) {182 inputParameterList->data.F32[i] = gsl_vector_get(params, i);183 }184 }185 186 // Call the user-supplied function.187 tmpf = evalModel(inputParameterList, coord);188 189 // Free allocated memory and return the value of the function.190 psFree(inputParameterList);191 return (tmpf);192 }193 194 /******************************************************************************195 p_psMinFuncDeriv(*params, *funcData): a GSL-like wrapper for the196 user-supplied procedure which calculates the derviative of the function to be197 minimized.198 199 params: The parameters of the function to be minimized. These will be200 varied by GSL in order to minimize the function.201 202 funcData: a private psLib struct which contains the data point to be203 minimized, the function and derivative function pointers, an initial204 guess at the parameters, an option parameter mask, etc.205 206 df: we calculate the derivative of the function w.r.t. to each parameter207 in "params" and return those derivatives in this psVector.208 *****************************************************************************/209 void p_psMinFuncDeriv(const gsl_vector * params, void *funcData, gsl_vector * df)210 {211 int i; // Loop index variable.212 int j; // Loop index variable.213 float tmpf; // Temporary floating point variable.214 const psVector* restrict coord = ((psMinimizeData* ) funcData)->coord;215 const psVector* restrict mask = ((psMinimizeData* ) funcData)->paramMask;216 psVector* restrict initialGuess = ((psMinimizeData* ) funcData)->initialGuess;217 psMinimizeFunctionDeriv d_evalModel = ((psMinimizeData* ) funcData)->d_evalModel;218 psVector* inputParameterList = NULL;219 220 // The GSL routines will call this function with the masked parameters221 // removed. However, the user-supplied function (to be modified) does not222 // have those parameters removed. Here will create a new parameter list223 // with the masked parameters added (we expand initialGuess).224 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);225 if (mask != NULL) {226 j = 0;227 for (i = 0; i < mask->n; i++) {228 if (mask->data.U8[i] != 0) {229 inputParameterList->data.F32[i] = initialGuess->data.F32[i];230 } else {231 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);232 }233 }234 } else {235 for (i = 0; i < initialGuess->n; i++) {236 inputParameterList->data.F32[i] = gsl_vector_get(params, i);237 }238 }239 240 // Evaluate the derivative w.r.t. each parameter.241 // NOTE: we can probably remove the calls for masked parameters.242 for (i = 0; i < initialGuess->n; i++) {243 tmpf = d_evalModel(inputParameterList, coord, i);244 gsl_vector_set(df, i, tmpf);245 }246 247 // Free allocated memory.248 psFree(inputParameterList);249 }250 251 /******************************************************************************252 Compute both p_psMinFunc and p_psMinFuncDeriv together.253 *****************************************************************************/254 void p_psMinFuncFuncDeriv(const gsl_vector * params, void *funcData, double *f, gsl_vector * df)255 {256 *f = p_psMinFunc(params, funcData);257 p_psMinFuncDeriv(params, funcData, df);258 }259 260 // The first argument to evalModel() and d_evalModel() specifies the data261 // point. It must have the same size as the second dimension of *domain.262 // The second argument must have the same size as *initialGuess and263 // *paramMask.264 265 /******************************************************************************266 p_psMinChi2Func(*x, *funcData, *outdata): We use the GSL procedure267 gsl_multifit_fdfsolver_iterate() to fit an arbitrary function, supplied by268 the user, to a set of data points. That GSL procedure requires the function269 to be fit to be in a different format than the psLib format. The purpose of270 this procedure is to serve as a GSL-format wrapper for the user-supplied271 procedure which is to be fit to the data.272 273 params: These are the parameters which are to be varied by GSL in order274 to minimize chi2 over the data set.275 276 funcData: this data structure contains the input values over which the277 function will be evaluated, the expected value of the function at278 those points, the amount of error tolerable at those points, a mask279 vector which specifies which parameters to the function are to be280 constant, and an initial guess at the parameters.281 282 outData: The function is evaluated at each point, then subtract the283 expected value and divide by the error.284 *****************************************************************************/285 int p_psMinChi2Func(const gsl_vector * params, void *funcData, gsl_vector * outData)286 {287 int i; // Loop index variable.288 int j; // Loop index variable.289 float tmpf; // Temporary floating point variable.290 const psImage* restrict domain = ((psMinChi2Data* ) funcData)->domain;291 const psVector* restrict data = ((psMinChi2Data* ) funcData)->data;292 const psVector* restrict errors = ((psMinChi2Data* ) funcData)->errors;293 const psVector* restrict mask = ((psMinChi2Data* ) funcData)->paramMask;294 psVector* restrict initialGuess = ((psMinChi2Data* ) funcData)->initialGuess;295 psMinimizeFunction evalModel = ((psMinChi2Data* ) funcData)->evalModel;296 psVector* inputParameterList = NULL;297 psVector* tmpVecPtr = NULL;298 299 tmpVecPtr = psVectorAlloc(domain->numCols, PS_TYPE_F32);300 301 // The GSL routines will call this function with the masked parameters302 // removed. However, the user-supplied function (to be modified) does not303 // have those parameters removed. Here will create a new parameter list304 // with the masked parameters added (we expand initialGuess).305 306 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);307 if (mask != NULL) {308 j = 0;309 for (i = 0; i < mask->n; i++) {310 if (mask->data.U8[i] != 0) {311 inputParameterList->data.F32[i] = initialGuess->data.F32[i];312 } else {313 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);314 }315 }316 } else {317 for (i = 0; i < initialGuess->n; i++) {318 inputParameterList->data.F32[i] = gsl_vector_get(params, i);319 }320 }321 322 // Evaluate the function at each data point.323 for (i = 0; i < domain->numRows; i++) {324 for (j = 0; j < domain->numCols; j++) {325 tmpVecPtr->data.F32[j] = domain->data.F32[i][j];326 }327 tmpf = evalModel(tmpVecPtr, inputParameterList);328 329 gsl_vector_set(outData, i, (tmpf - data->data.F32[i]) / errors->data.F32[i]);330 }331 332 // Free allocated memory.333 psFree(inputParameterList);334 psFree(tmpVecPtr);335 336 return GSL_SUCCESS;337 }338 339 /******************************************************************************340 p_psMinChi2FuncDeriv(*x, *funcData, *outdata): a GSL-like wrapper for the341 user-supplied procedure which calculates the derviative of the function to be342 minimized.343 params: These are the parameters which are to be varied by GSL in order344 to minimize chi2 over the data set.345 346 funcData: this data structure contains the input values over which the347 function will be evaluated, the expected value of the function at348 those points, the amount of error tolerable at those points, a mask349 vector which specifies which parameters to the function are to be350 constant, and an initial guess at the parameters.351 352 J: The derivative is evaluated at each point and w.r.t. each parameter353 and returned in this data structure.354 *****************************************************************************/355 int p_psMinChi2FuncDeriv(const gsl_vector * params, void *funcData, gsl_matrix * J)356 {357 const psImage* restrict domain = ((psMinChi2Data* ) funcData)->domain;358 const psVector* restrict errors = ((psMinChi2Data* ) funcData)->errors;359 const psVector* restrict mask = ((psMinChi2Data* ) funcData)->paramMask;360 psVector* restrict initialGuess = ((psMinChi2Data* ) funcData)->initialGuess;361 psVector* inputParameterList = NULL;362 psVector* tmpVecPtr = NULL;363 psMinimizeFunctionDeriv d_evalModel = ((psMinChi2Data* ) funcData)->d_evalModel;364 365 size_t i;366 int j;367 float tmpf;368 369 tmpVecPtr = psVectorAlloc(domain->numCols, PS_TYPE_F32);370 371 // The GSL routines will call this functions with the masked parameters372 // removed. However, the user-supplied function (to be modified) does not373 // have those parameters removed. Here will create a new parameter list374 // with the masked parameters added (we expand initialGuess).375 376 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);377 if (mask != NULL) {378 j = 0;379 for (i = 0; i < mask->n; i++) {380 if (mask->data.U8[i] != 0) {381 inputParameterList->data.F32[i] = initialGuess->data.F32[i];382 } else {383 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);384 }385 }386 } else {387 for (i = 0; i < initialGuess->n; i++) {388 inputParameterList->data.F32[i] = gsl_vector_get(params, i);389 }390 }391 392 // Evaluate the derivtaive at each data point, and w.r.t. each parameter.393 for (i = 0; i < domain->numRows; i++) {394 for (j = 0; j < tmpVecPtr->n; j++) {395 tmpVecPtr->data.F32[j] = domain->data.F32[i][j];396 }397 398 for (j = 0; j < inputParameterList->n; j++) {399 tmpf = d_evalModel(tmpVecPtr, inputParameterList, j);400 gsl_matrix_set(J, i, j, (tmpf / errors->data.F32[i]));401 }402 }403 404 psFree(inputParameterList);405 psFree(tmpVecPtr);406 return GSL_SUCCESS;407 }408 409 int p_psMinChi2FuncFuncDeriv(const gsl_vector * params, void *funcData, gsl_vector * f, gsl_matrix * J)410 {411 p_psMinChi2Func(params, funcData, f);412 p_psMinChi2FuncDeriv(params, funcData, J);413 414 return GSL_SUCCESS;415 }416 114 417 115 /****************************************************************************** … … 433 131 434 132 /****************************************************************************** 435 p_psBuildSums1D(x): this routine returns a psVector with "x" elements. The133 VectorNormalizeGen(): this routine returns a psVector with "x" elements. The 436 134 values of the vector will be scaled uniformly between -1.0 and 1.0. 437 *****************************************************************************/438 psVector* psBuildImageScalingFactors(int x)135 *****************************************************************************/ 136 psVector* VectorNormalizeGen(int x) 439 137 { 440 int i = 0; // loop index variable.441 psVector * imageScalingFactors= NULL;442 443 imageScalingFactors= psVectorAlloc(x, PS_TYPE_F32);138 int i = 0; 139 psVector *tmp = NULL; 140 141 tmp = psVectorAlloc(x, PS_TYPE_F32); 444 142 445 143 for (i = 0; i < x; i++) { 446 imageScalingFactors->data.F32[i] = (((float)2 * i) / ((float)x)) - 1.0; 447 } 448 449 return (imageScalingFactors); 450 } 144 tmp->data.F32[i] = (((float)2 * i) / ((float)x)) - 1.0; 145 } 146 147 return (tmp); 148 } 149 150 /*****************************************************************************/ 151 /* FUNCTION IMPLEMENTATION - PUBLIC */ 152 /*****************************************************************************/ 451 153 452 154 /****************************************************************************** 453 CURRENTLY NOT IN USE. 454 455 p_psPolyOrderCheck(A, N, *indx, *B, polyOrder,*flag) This routine checks if 456 all polyOrder-th terms in the polyOrder-th order sky background polynomial 457 defined by the coefficients in the array B[] are consistent with zero. If 458 true, then *flag is set to 1. Otherwise, *flag is set to 0. The matrix 459 inversion code in the middle of this procedure draws from Numerical Recipes 460 in C page 48. 461 Input: 462 A This is the LUD decomposition of the original matrix A. 463 N The size of the matrix (plus 1, actually, since offset 1). 464 indx misc Numerical Recipes data structure. 465 B The coefficients of the sky polynomial. 466 polyOrder The degree of the sky polynomial. 467 Output: 468 *flag Set this to 1 if we must recalculate the coefficients. 469 *****************************************************************************/ 470 void p_psPolyOrderCheck(float **A, int N, int *indx, float *B, int polyOrder, int *flag) 471 { 472 float **y = NULL; // This 2-D matrix will hold A^-1 473 float *col = NULL; // misc NumerRecipes data structure 474 float *error = NULL; // will hold the sqrt() of the 475 476 // diagonal of y[][]. 477 int i = 0; // loop-index variable 478 int j = 0; // loop-index variable 479 int numPolyTerms = 0; // The number of terms in the 480 481 // polynomial. 482 int lastTerm = 0; // The index location of the first 483 484 // n-th order term in array B[]. 485 int firstTerm = 0; // Index location of last such term. 486 487 // Allocate the necessary data structures for this procedure... 488 error = (float *)psAlloc((N + 1) * sizeof(float)); 489 col = (float *)psAlloc((N + 1) * sizeof(float)); 490 y = (float **)psAlloc((N + 1) * sizeof(float *)); 491 for (i = 1; i <= N; i++) { 492 y[i] = (float *)psAlloc((N + 1) * sizeof(float)); 493 } 494 495 // Invert the matrix A and put the result in y[][]. This code is taken 496 // from Numerical Recipes in C page 48. 497 for (j = 1; j <= N; j++) { 498 for (i = 1; i <= N; i++) { 499 col[i] = 0.0; 500 } 501 col[j] = 1.0; 502 // NOTE: substitue the LUD rotine 503 // lubksb(A, N, indx, col); 504 for (i = 1; i <= N; i++) { 505 y[i][j] = col[i]; 506 } 507 } 508 509 // Determine where the first n-th order (in this comment, n equals 510 // polyOrder) polynomial term is stored in the matrix B[], and also were 511 // the last n-order term is stored. Then we loop over all the n-order 512 // terms and check if they are consistent with zero. 513 514 numPolyTerms = (((polyOrder + 1) * (polyOrder + 2)) / 2); 515 lastTerm = numPolyTerms + 1; 516 firstTerm = lastTerm - polyOrder; 517 *flag = 1; 518 for (i = firstTerm; i <= lastTerm; i++) { 519 #ifdef DARWIN 520 error[i] = (float)sqrt(y[i][i]); 521 #else 522 523 error[i] = sqrtf(y[i][i]); 524 #endif 525 526 if (!((B[i] <= (2.0f * error[i])) && ((-2.0f * error[i]) <= B[i]))) { 527 *flag = 0; 528 } 529 } 530 531 // Free all memory allocated in this routine. 532 psFree(error); 533 psFree(col); 534 for (j = 1; j <= N; j++) { 535 psFree(y[j]); 536 } 537 psFree(y); 538 } 539 540 /*****************************************************************************/ 541 /* FUNCTION IMPLEMENTATION - PUBLIC */ 542 /*****************************************************************************/ 543 544 /****************************************************************************** 545 psMinimize(initialGuess, myFunction, myFunctionDeriv, coord, paramMask): 546 547 This routine must minimize an arbitrary function; it determines the set of 548 parameters of that function such that the ... 549 *****************************************************************************/ 550 psVector* psMinimize(psVector* restrict initialGuess, 551 psMinimizeFunction myFunction, 552 psMinimizeFunctionDeriv myFunctionDeriv, 553 const psVector* restrict coord, 554 const psVector* restrict paramMask) 555 { 556 int status; 557 int i = 0; 558 int j = 0; 559 int iter = 0; 560 gsl_multimin_function_fdf f; 561 const gsl_multimin_fdfminimizer_type *T; 562 gsl_multimin_fdfminimizer *s; 563 psMinimizeData inputData; 564 gsl_vector *x; 565 566 psTrace(".psLib.dataManip.psMinimize", 4, 567 "---- psMinimize() begin ----\n"); 568 569 PS_CHECK_NULL_VECTOR(initialGuess); 570 PS_CHECK_EMPTY_VECTOR(initialGuess); 571 PS_CHECK_NULL_VECTOR(coord); 572 PS_CHECK_EMPTY_VECTOR(coord); 573 if (paramMask != NULL) { 574 PS_CHECK_NULL_VECTOR(paramMask); 575 PS_CHECK_EMPTY_VECTOR(paramMask); 576 PS_CHECK_VECTOR_SIZE_EQUAL(initialGuess, paramMask); 577 } 578 579 inputData.initialGuess = initialGuess; 580 inputData.coord = coord; 581 inputData.paramMask = paramMask; 582 inputData.evalModel = myFunction; 583 inputData.d_evalModel = myFunctionDeriv; 584 inputData.paramCount = 0; 585 586 // If the user supplied a parameter mask, then count the number of 587 // non-masked elements. This will be used later in allocating a vector 588 // for the parameters. 589 if (paramMask != NULL) { 590 for (i = 0; i < paramMask->n; i++) { 591 if (paramMask->data.U8[i] != 0) { 592 inputData.paramCount++; 593 } 594 } 595 } else { 596 inputData.paramCount = initialGuess->n; 597 } 598 599 // The initial guess at the parameters for the function are written into 600 // the vector inputParameterList. If the paramMask is not NULL, then 601 // masked parameters are masked out. 602 x = gsl_vector_alloc(inputData.paramCount); 603 if (paramMask != NULL) { 604 j = 0; 605 for (i = 0; i < initialGuess->n; i++) { 606 if (paramMask->data.U8[i] == 0) { 607 gsl_vector_set(x, j++, initialGuess->data.F32[i]); 608 } 609 } 610 } else { 611 for (i = 0; i < initialGuess->n; i++) { 612 gsl_vector_set(x, i, initialGuess->data.F32[i]); 613 } 614 } 615 f.f = &p_psMinFunc; 616 f.df = &p_psMinFuncDeriv; 617 f.fdf = &p_psMinFuncFuncDeriv; 618 f.n = inputData.paramCount; 619 f.params = &inputData; 620 621 T = gsl_multimin_fdfminimizer_conjugate_fr; 622 s = gsl_multimin_fdfminimizer_alloc(T, inputData.paramCount); 623 gsl_multimin_fdfminimizer_set(s, &f, x, 0.01, 1e-4); 624 do { 625 iter++; 626 status = gsl_multimin_fdfminimizer_iterate(s); 627 628 if (status) 629 break; 630 631 status = gsl_multimin_test_gradient(s->gradient, 1e-3); 632 633 if (status == GSL_SUCCESS) 634 printf("Minimum found at:\n"); 635 636 } while (status == GSL_CONTINUE && iter < MAX_MINIMIZE_ITERATIONS); 637 638 // In the above steps we had removed the masked elements from the 639 // the solver. This next code blocks puts those masked elements 640 // into the solution. 641 if (paramMask != NULL) { 642 j = 0; 643 for (i = 0; i < initialGuess->n; i++) { 644 if (paramMask->data.U8[i] == 0) { 645 initialGuess->data.F32[i] = gsl_vector_get(s->x, j++); 646 } else { 647 initialGuess->data.F32[i] = initialGuess->data.F32[i]; 648 } 649 } 650 } else { 651 for (i = 0; i < initialGuess->n; i++) { 652 initialGuess->data.F32[i] = gsl_vector_get(s->x, i); 653 } 654 } 655 psTrace(".psLib.dataManip.psMinimize", 4, 656 "---- psMinimize() end ----\n"); 657 return (initialGuess); 658 } 659 660 /****************************************************************************** 661 This routine must determine the parameters of an arbitrary function 662 such that they best fit the supplied data points. 663 *****************************************************************************/ 664 psVector* psMinimizeChi2(psMinimizeFunction evalModel, 665 psMinimizeFunctionDeriv DevalModel, 666 const psImage* restrict domain, 667 const psVector* restrict data, 668 const psVector* restrict errors, 669 psVector* restrict initialGuess, 670 const psVector* restrict paramMask, 671 float *chiSq) 672 { 673 int numData = domain->numRows; // Number of data points 674 int status; // Return status for the GSL solver. 675 int i = 0; // Loop index variable. 676 int j = 0; // Loop index variable. 677 int iter = 0; // Iteration counter. 678 gsl_multifit_function_fdf f; // GSL structure that contains the 679 680 // functions/derivative to be solved. 681 double *xInit = NULL; // The initial guess at the parameters 682 683 // with masked parameters removed. 684 const gsl_multifit_fdfsolver_type *T; 685 686 // This tells GSL to use the Levenberg- 687 // Marquardt algorithm for chi2 688 // minimization. 689 gsl_multifit_fdfsolver *s; // GSL data structure. 690 psMinChi2Data inputData; 691 float chiSqOld = 0.0; 692 693 psTrace(".psLib.dataManip.psMinimizeChi2", 4, 694 "---- psMinimizeChi2() begin ----\n"); 695 696 PS_CHECK_NULL_IMAGE(domain); 697 PS_CHECK_EMPTY_IMAGE(domain); 698 PS_CHECK_NULL_VECTOR(data); 699 PS_CHECK_EMPTY_VECTOR(data); 700 PS_CHECK_NULL_VECTOR(errors); 701 PS_CHECK_EMPTY_VECTOR(errors); 702 PS_CHECK_NULL_VECTOR(initialGuess); 703 PS_CHECK_EMPTY_VECTOR(initialGuess); 704 PS_CHECK_VECTOR_SIZE_EQUAL(data, errors); 705 if (domain->numRows != data->n) { 706 psAbort(__func__, "Number of data points and data values not equal."); 707 } 708 if (paramMask != NULL) { 709 PS_CHECK_NULL_VECTOR(paramMask); 710 PS_CHECK_EMPTY_VECTOR(paramMask); 711 PS_CHECK_VECTOR_SIZE_EQUAL(initialGuess, paramMask); 712 } 713 714 inputData.n = numData; 715 inputData.paramCount = 0; 716 inputData.initialGuess = initialGuess; 717 inputData.domain = domain; 718 inputData.data = data; 719 inputData.errors = errors; 720 inputData.paramMask = paramMask; 721 inputData.evalModel = evalModel; 722 inputData.d_evalModel = DevalModel; 723 724 // If the user supplied a parameter mask, then count the number of 725 // non-masked elements. This will be used later in allocating a vector 726 // for the parameters. 727 if (paramMask != NULL) { 728 for (i = 0; i < paramMask->n; i++) { 729 if (paramMask->data.U8[i] != 0) { 730 inputData.paramCount++; 731 } 732 } 733 } else { 734 inputData.paramCount = initialGuess->n; 735 } 736 737 // The initial guess at the parameters for the function are written into 738 // the vector inputParameterList. If the paramMask is not NULL, then those 739 // parameters are masked out. 740 xInit = (double *)psAlloc(inputData.paramCount * sizeof(double)); 741 if (paramMask != NULL) { 742 j = 0; 743 for (i = 0; i < initialGuess->n; i++) { 744 if (paramMask->data.U8[i] == 0) { 745 xInit[j++] = initialGuess->data.F32[i]; 746 } 747 } 748 } else { 749 for (i = 0; i < initialGuess->n; i++) { 750 xInit[i] = initialGuess->data.F32[i]; 751 } 752 } 753 754 const gsl_rng_type *type; 755 gsl_rng *r; 756 757 gsl_rng_env_setup(); 758 759 type = gsl_rng_default; 760 r = gsl_rng_alloc(type); 761 762 // Initialize the main data structure used by the GSL solver. This will 763 // contain pointers to the function to be minimized, it's derivative 764 // function, the number of data points, the number of free parameters, 765 // and the data structures those functions use. 766 767 f.f = &p_psMinChi2Func; 768 f.df = &p_psMinChi2FuncDeriv; 769 f.fdf = &p_psMinChi2FuncFuncDeriv; 770 f.n = numData; 771 f.p = inputData.paramCount; 772 f.params = &inputData; 773 774 gsl_vector_view x = gsl_vector_view_array(xInit, inputData.paramCount); 775 776 T = gsl_multifit_fdfsolver_lmsder; 777 s = gsl_multifit_fdfsolver_alloc(T, numData, inputData.paramCount); 778 gsl_multifit_fdfsolver_set(s, &f, &x.vector); 779 *chiSq = 0.0; 780 chiSqOld = 0.0; 781 do { 782 iter++; 783 for (i = 0; i < initialGuess->n; i++) { 784 printf("Iteration %d: parameter %d is %.3f\n", iter, i, gsl_vector_get(s->x, i)); 785 } 786 // Perform an iteration of the GSL solver. 787 status = gsl_multifit_fdfsolver_iterate(s); 788 printf("gsl_multifit_fdfsolver_iterate() status is %s\n", gsl_strerror(status)); 789 for (i = 0; i < initialGuess->n; i++) { 790 printf("Iteration %d: parameter %d is %.3f\n", iter, i, gsl_vector_get(s->x, i)); 791 } 792 793 // If there was a problem, abort. 794 if (status) { 795 psAbort(__func__, "gsl_multifit_fdfsolver_iterate(%s)\n", gsl_strerror(status)); 796 } 797 // Test if the parameters changed by a small enough amount. 798 // NOTE: This wasn't working right when the parameters fit exactly. 799 // Figure out why. 800 // status = gsl_multifit_test_delta(s->dx, s->x, 1e-4, 1e-4); 801 802 // We test for convergence if chiSquared changes by less than 1.0 803 // as specified in the ADD. 804 *chiSq = gsl_blas_dnrm2(s->f); 805 printf("psMinimize.c: chiSq is %.3f\n", *chiSq); 806 if (fabs(*chiSq - chiSqOld) < 1.0) { 807 status = GSL_SUCCESS; 808 } else { 809 status = GSL_CONTINUE; 810 } 811 chiSqOld = *chiSq; 812 813 } while (status == GSL_CONTINUE && iter < MAX_LMM_ITERATIONS); 814 815 // In the above steps we had removed the masked elements from the 816 // the solver. This next code blocks puts those masked elements 817 // into the solution. 818 if (paramMask != NULL) { 819 j = 0; 820 for (i = 0; i < initialGuess->n; i++) { 821 if (paramMask->data.U8[i] == 0) { 822 initialGuess->data.F32[i] = gsl_vector_get(s->x, j++); 823 } else { 824 initialGuess->data.F32[i] = initialGuess->data.F32[i]; 825 } 826 } 827 } else { 828 for (i = 0; i < initialGuess->n; i++) { 829 initialGuess->data.F32[i] = gsl_vector_get(s->x, i); 830 } 831 } 832 833 // Calculate the chi-squared for the derived solution. 834 *chiSq = gsl_blas_dnrm2(s->f); 835 836 // Free all allocated memory 837 // NOTE: Free x. 838 gsl_multifit_fdfsolver_free(s); 839 psFree(xInit); 840 841 // Bye bye. 842 psTrace(".psLib.dataManip.psMinimizeChi2", 4, 843 "---- psMinimizeChi2() end ----\n"); 844 return (initialGuess); 845 } 846 847 /****************************************************************************** 848 This routine will take an procedure which calculates an arbitrary function 849 and it's derivative and minimize it. 850 851 XXX: Do this: 852 After checking that all entries in the paramMask are 1 or 0, when 853 forming the A matrix from alpha, try this: 854 855 A[i][i] = (1 + lambda*paramask[i]) * alpha[i][i]; 856 *****************************************************************************/ 857 bool psMinimizeLM(psMinimization *min, 858 psImage *covar, 859 psVector *params, 860 const psVector *paramMask, 861 const psArray *coords, 862 psMinimizeLMFunc func) 863 { 864 psVector *beta = psVectorAlloc(params->n, PS_TYPE_F64); 865 psVector *perm = psVectorAlloc(params->n, PS_TYPE_F64); 866 psVector *newParamsF64 = psVectorAlloc(params->n, PS_TYPE_F64); 867 psVector *newParamsF32 = psVectorAlloc(params->n, PS_TYPE_F32); 868 psVector *origParams = psVectorAlloc(params->n, PS_TYPE_F32); 869 psImage *alpha = psImageAlloc(params->n, params->n, PS_TYPE_F32); 870 psImage *A = psImageAlloc(params->n, params->n, PS_TYPE_F64); 871 psImage *aOut = psImageAlloc(params->n, params->n, PS_TYPE_F64); 872 psVector *deriv = psVectorAlloc(params->n, PS_TYPE_F32); 873 psVector *newDeriv = psVectorAlloc(params->n, PS_TYPE_F32); 874 psVector *NRparams = psVectorAlloc(params->n, PS_TYPE_F32); 875 int i; 876 int j; 877 int k; 878 float newValue; 879 float oldValue; 880 float lamda = 0.00005; 881 882 psTrace(".psLib.dataManip.psMinimizeLM", 4, 883 "---- psMinimizeLM() begin ----\n"); 884 psTrace(".psLib.dataManip.psMinimizeLM", 6, 885 "min->maxIter is %d\n", min->maxIter); 886 psTrace(".psLib.dataManip.psMinimizeLM", 6, 887 "min->tol is %f\n", min->tol); 888 889 for (i=0;i<params->n;i++) { 890 origParams->data.F32[i] = params->data.F32[i]; 891 } 892 893 min->lastDelta = HUGE; 894 min->lastDelta = 12345.0; 895 min->iter = 0; 896 897 while ((min->lastDelta > min->tol) && 898 (min->iter < min->maxIter)) { 899 psTrace(".psLib.dataManip.psMinimizeLM", 4, 900 "------------------------------------------------------\n"); 901 psTrace(".psLib.dataManip.psMinimizeLM", 4, 902 "Iteration %d. Delta is %f\n", min->iter, min->lastDelta); 903 904 min->value = func(deriv, params, coords); 905 906 for (i=0;i<params->n;i++) { 907 psTrace(".psLib.dataManip.psMinimizeLM", 6, 908 "params->data.F32[%d] is %f.\n", i, params->data.F32[i]); 909 } 910 for (i=0;i<params->n;i++) { 911 psTrace(".psLib.dataManip.psMinimizeLM", 6, 912 "deriv->data.F32[%d] is %f.\n", i, deriv->data.F32[i]); 913 } 914 psTrace(".psLib.dataManip.psMinimizeLM", 6, 915 "min->value is (%f)\n", min->value); 916 917 for (i=0;i<params->n;i++) { 918 if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) { 919 deriv->data.F32[i] = 0.0; 920 } 921 } 922 923 // Calculate the BETA vector. 924 for (i=0;i<params->n;i++) { 925 beta->data.F64[i] = (float) deriv->data.F32[i]; 926 psTrace(".psLib.dataManip.psMinimizeLM", 6, 927 "beta->data.F64[%d] is %f.\n", i, beta->data.F64[i]); 928 } 929 930 // Calculate the ALPHA matrix. 931 for (j=0;j<params->n;j++) { 932 for (k=0;k<params->n;k++) { 933 alpha->data.F32[j][k] = deriv->data.F32[k] * 934 deriv->data.F32[j]; 935 } 936 } 937 938 // Calculate the matrix A. 939 for (j=0;j<params->n;j++) { 940 for (k=0;k<params->n;k++) { 941 if (j == k) { 942 A->data.F64[j][k] = 943 (double) ((1.0 + lamda) * alpha->data.F32[j][k]); 944 } else { 945 A->data.F64[j][k] = (double) alpha->data.F32[j][k]; 946 } 947 } 948 } 949 for (j=0;j<params->n;j++) { 950 psTrace(".psLib.dataManip.psMinimizeLM", 6, "Matrix A[][]:\n"); 951 for (k=0;k<params->n;k++) { 952 psTrace(".psLib.dataManip.psMinimizeLM", 6, "%f ", A->data.F64[j][k]); 953 } 954 psTrace(".psLib.dataManip.psMinimizeLM", 6, "Matrix A[][]:\n"); 955 } 956 957 // Solve A * alpha = Beta 958 aOut = psMatrixLUD(aOut, perm, A); 959 newParamsF64 = psMatrixLUSolve(newParamsF64, aOut, beta, perm); 960 961 // Mask any masked parameters. 962 for (i=0;i<params->n;i++) { 963 if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) { 964 newParamsF64->data.F64[i] = (double) origParams->data.F32[i]; 965 } 966 newParamsF32->data.F32[i] = (float) newParamsF64->data.F64[i]; 967 968 psTrace(".psLib.dataManip.psMinimizeLM", 6, 969 "newParamsF32->data.F32[%d] is %f.\n", i, newParamsF32->data.F32[i]); 970 NRparams->data.F32[i] = params->data.F32[i] - newParamsF32->data.F32[i]; 971 } 972 973 psTrace(".psLib.dataManip.psMinimizeLM", 6, 974 "Calling func() with new parameters:\n"); 975 for (i=0;i<params->n;i++) { 976 psTrace(".psLib.dataManip.psMinimizeLM", 6, 977 "NRparams->data.F32[%d] is %f.\n", i, NRparams->data.F32[i]); 978 } 979 980 oldValue = min->value; 981 newValue = func(deriv, NRparams, coords); 982 psTrace(".psLib.dataManip.psMinimizeLM", 4, 983 "old/new values are (%f, %f)\n", oldValue, newValue); 984 for (i=0;i<params->n;i++) { 985 if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) { 986 deriv->data.F32[i] = 0.0; 987 } 988 } 989 990 if (oldValue > newValue) { 991 min->lastDelta = oldValue - newValue; 992 min->value = newValue; 993 994 // No need to check the paramMask here since we already did so 995 // before the last function evaluation. 996 for (i=0;i<params->n;i++) { 997 // params->data.F32[i] = (float) newParamsF64->data.F64[i]; 998 params->data.F32[i] = (float) NRparams->data.F32[i]; 999 } 1000 min->value = func(deriv, params, coords); 1001 1002 lamda*= 0.1; 1003 } else { 1004 lamda*= 10.0; 1005 } 1006 psTrace(".psLib.dataManip.psMinimizeLM", 4, 1007 "lamda is %f\n", lamda); 1008 min->iter++; 1009 } 1010 psFree(beta); 1011 psFree(perm); 1012 psFree(newParamsF64); 1013 psFree(newParamsF32); 1014 psFree(origParams); 1015 psFree(alpha); 1016 psFree(A); 1017 psFree(aOut); 1018 psFree(deriv); 1019 psFree(newDeriv); 1020 1021 if ((min->iter < min->maxIter) || 1022 (min->lastDelta <= min->tol)) { 1023 return(true); 1024 } 1025 1026 psTrace(".psLib.dataManip.psMinimizeLM", 4, 1027 "---- psMinimizeLM() end (false) ----\n"); 1028 return(false); 1029 } 1030 1031 /****************************************************************************** 1032 This routine will take an procedure which calculates an arbitrary function 1033 and it's derivative and minimize the chi-squared match between that function 1034 at the specified coords and the specified value at those coords. 155 psMinimizeLMChi2(): This routine will take an procedure which calculates an 156 arbitrary function and it's derivative and minimize the chi-squared match 157 between that function at the specified coords and the specified value at 158 those coords. 1035 159 1036 160 XXX: Do this: … … 1265 389 1266 390 /****************************************************************************** 1267 This routine must fit a polynomial of degree myPoly to the data points 1268 (x, y) and return the coefficients of that polynomial, as well as the 1269 error for each data point (yErr).391 psVectorFitPolynomial1D(): This routine must fit a polynomial of degree 392 myPoly to the data points (x, y) and return the coefficients of that 393 polynomial, as well as the error for each data point (yErr). 1270 394 1271 395 XXX: NOTE: yErr is currently ignored. … … 1273 397 psPolynomial1D* psVectorFitPolynomial1D(psPolynomial1D* myPoly, 1274 398 const psVector* restrict x, 1275 const psVector* restrict y, const psVector* restrict yErr) 399 const psVector* restrict y, 400 const psVector* restrict yErr) 1276 401 { 1277 402 int polyOrder = myPoly->n; … … 1381 506 1382 507 #define STEP_SIZE 0.10 1383 /******************************************************************************1384 This routine takes as input an arbitrary function, and the parameter to1385 vary. This function produces as output a bracket [a, b, c] such that1386 f(b) is less than f(a) and f(b).1387 1388 Algorithm: XXX completely ad hoc: start with the user-supplied starting1389 parameter and call that b. Calculate a/c as a fractional amount1390 smaller/larger than b. Repeat this process until a local minimum is1391 found.1392 *****************************************************************************/1393 psVector *p_psDetermineBracketOld(psVector *params,1394 int dim,1395 const psArray *coords,1396 psMinimizePowellFunc func)1397 {1398 float a = 0.0;1399 float b = 0.0;1400 float c = 0.0;1401 float fa = 0.0;1402 float fb = 0.0;1403 float fc = 0.0;1404 int iter = 100;1405 float aDir = 0.0;1406 float cDir = 0.0;1407 float new_aDir = 0.0;1408 float new_cDir = 0.0;1409 psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);1410 float stepSize = params->data.F32[dim] * STEP_SIZE;1411 // float initialParam = params->data.F32[dim];1412 1413 if (stepSize == 0.0) {1414 stepSize = 1.0;1415 }1416 a = b = c = params->data.F32[0];1417 a-= stepSize;1418 c+= stepSize;1419 1420 params->data.F32[dim] = a;1421 fa = func(params, coords);1422 params->data.F32[dim] = b;1423 fb = func(params, coords);1424 params->data.F32[dim] = c;1425 fc = func(params, coords);1426 if (fa < fb) {1427 aDir = -1;1428 } else {1429 aDir = 1;1430 }1431 1432 if (fc < fb) {1433 cDir = -1;1434 } else {1435 cDir = 1;1436 }1437 1438 while (iter > 0) {1439 if ((b < a) && (b < c)) {1440 bracket->data.F32[0] = a;1441 bracket->data.F32[1] = b;1442 bracket->data.F32[2] = c;1443 return(bracket);1444 }1445 stepSize*= (1.0 + stepSize);1446 a = a - stepSize;1447 c = c + stepSize;1448 1449 params->data.F32[dim] = a;1450 fa = func(params, coords);1451 params->data.F32[dim] = c;1452 fc = func(params, coords);1453 1454 //printf("HMMM(%d): (%f %f %f) (%f %f %f)\n", iter, a, b, c, fa, fb, fc);1455 1456 if (fa < fb) {1457 new_aDir = -1;1458 } else {1459 new_aDir = 1;1460 }1461 1462 if (fc < fb) {1463 new_cDir = -1;1464 } else {1465 new_cDir = 1;1466 }1467 if ((new_aDir == 1) && (aDir == -1)) {1468 bracket->data.F32[0] = a;1469 bracket->data.F32[1] = b;1470 bracket->data.F32[2] = c;1471 return(bracket);1472 }1473 1474 if ((new_cDir == 1) && (cDir == -1)) {1475 bracket->data.F32[0] = a;1476 bracket->data.F32[1] = b;1477 bracket->data.F32[2] = c;1478 return(bracket);1479 }1480 aDir = new_aDir;1481 cDir = new_cDir;1482 iter--;1483 }1484 psFree(bracket);1485 return(NULL);1486 }1487 1488 508 /****************************************************************************** 1489 509 This routine takes as input an arbitrary function, and the parameter to … … 1666 686 } 1667 687 1668 1669 /******************************************************************************1670 This routine must minimize a possibly multi-dimensional function1671 (several parameters) along a single dimension.1672 *****************************************************************************/1673 bool psMinimize1DFunc(psMinimization *min,1674 psVector *params,1675 int dim,1676 const psArray *coords,1677 psMinimizePowellFunc func)1678 {1679 psVector *bracket;1680 float a = 0.0;1681 float b = 0.0;1682 float c = 0.0;1683 float n = 0.0;1684 float fa = 0.0;1685 float fb = 0.0;1686 float fc = 0.0;1687 float fn = 0.0;1688 // float initialParam = params->data.F32[dim];1689 1690 bracket = p_psDetermineBracketOld(params, dim, coords, func);1691 if (bracket == NULL) {1692 psAbort(__func__, "Could not bracket minimum.");1693 }1694 1695 min->iter = 0;1696 while (min->iter < min->maxIter) {1697 min->iter++;1698 //printf("psMinimize1DFunc(): iteration %d\n", min->iter);1699 a = bracket->data.F32[0];1700 b = bracket->data.F32[1];1701 c = bracket->data.F32[2];1702 1703 params->data.F32[dim] = a;1704 fa = func(params, coords);1705 params->data.F32[dim] = b;1706 fb = func(params, coords);1707 params->data.F32[dim] = c;1708 fc = func(params, coords);1709 //printf("Iteration %d: f(%f %f %f) is (%f %f %f)\n", min->iter, a, b, c, fa, fb, fc);1710 1711 // We determine which is the biggest segment in [a,b,c] then split1712 // that with the point n.1713 if ((b-a) > (c-b)) {1714 // This is the golden section formula1715 params->data.F32[dim] = n = a + (0.69 * (b-a));1716 fn = func(params, coords);1717 if (fn > fb) {1718 // a = n, b = b, c = c1719 bracket->data.F32[0] = n;1720 } else {1721 // a = a, b = n, c = b1722 bracket->data.F32[1] = n;1723 bracket->data.F32[2] = b;1724 }1725 } else {1726 params->data.F32[dim] = n = b + (0.69 * (c-b));1727 fn = func(params, coords);1728 if (fn > fb) {1729 // a = a, b = b, c = n1730 bracket->data.F32[2] = n;1731 } else {1732 // a = b, b = n, c = c1733 bracket->data.F32[0] = b;1734 bracket->data.F32[1] = n;1735 }1736 }1737 1738 if ((fabs(a-b) < min->tol) &&1739 (fabs(b-c) < min->tol)) {1740 // psFree(bracket);1741 // XXX: is this line correct?1742 params->data.F32[dim] = bracket->data.F32[1];1743 min->value = func(params, coords);1744 return(true);1745 }1746 }1747 1748 // psFree(bracket);1749 return(false);1750 }1751 688 1752 689 /****************************************************************************** … … 2168 1105 return(psMinimizePowell(min, params, paramMask, coords, myPowellChi2Func)); 2169 1106 } 1107 1108 -
trunk/psLib/src/math/psMinimize.c
r1831 r1836 9 9 * @author George Gusciora, MHPCC 10 10 * 11 * @version $Revision: 1.4 0$ $Name: not supported by cvs2svn $12 * @date $Date: 2004-09- 18 01:50:45$11 * @version $Revision: 1.41 $ $Name: not supported by cvs2svn $ 12 * @date $Date: 2004-09-20 23:16:10 $ 13 13 * 14 14 * Copyright 2004 Maui High Performance Computing Center, University of Hawaii … … 94 94 /*****************************************************************************/ 95 95 96 typedef struct97 {98 size_t n; // Number of data points points in domain.99 int paramCount; // Number of non-masked parameters.100 psVector* restrict initialGuess;101 const psImage* restrict domain;102 const psVector* restrict data;103 const psVector* restrict errors;104 const psVector* restrict paramMask;105 psMinimizeFunction evalModel;106 psMinimizeFunctionDeriv d_evalModel;107 }108 psMinChi2Data;109 110 typedef struct111 {112 int paramCount; // Number of non-masked parameters.113 psVector* restrict initialGuess;114 const psVector* restrict coord;115 const psVector* restrict paramMask;116 psMinimizeFunction evalModel;117 psMinimizeFunctionDeriv d_evalModel;118 }119 psMinimizeData;120 121 96 /*****************************************************************************/ 122 97 /* GLOBAL VARIABLES */ … … 137 112 /* FUNCTION IMPLEMENTATION - LOCAL */ 138 113 /*****************************************************************************/ 139 140 /******************************************************************************141 p_psMinFunc(*params, *funcData): We use the GSL procedure142 gsl_multimin_fdfminimizer_iterate() to minimize an arbitary function supplied143 by the user. That GSL procedure requires the function to be minimized to be144 in a different format than the psLib format. The purpose of this procedure145 is to serve as a GSL-format wrapper for the user-supplied procedure which is146 to be minimized.147 148 params: The parameters of the function to be minimized. These will be149 varied by GSL in order to minimize the function.150 151 funcData: a private psLib struct which contains the data point to be152 minimized, the function and derivative function pointers, an initial153 guess at the parameters, an option parameter mask, etc.154 *****************************************************************************/155 double p_psMinFunc(const gsl_vector * params, void *funcData)156 {157 int i; // Loop index variable.158 int j; // Loop index variable.159 float tmpf; // Temporary floating point variable.160 const psVector* restrict coord = ((psMinimizeData* ) funcData)->coord;161 const psVector* restrict mask = ((psMinimizeData* ) funcData)->paramMask;162 psVector* restrict initialGuess = ((psMinimizeData* ) funcData)->initialGuess;163 psMinimizeFunction evalModel = ((psMinimizeData* ) funcData)->evalModel;164 psVector* inputParameterList = NULL;165 166 // The GSL routines will call this function with the masked parameters167 // removed. However, the user-supplied function (to be modified) does not168 // have those parameters removed. Here will create a new parameter list169 // with the masked parameters added (we expand initialGuess).170 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);171 if (mask != NULL) {172 j = 0;173 for (i = 0; i < mask->n; i++) {174 if (mask->data.U8[i] != 0) {175 inputParameterList->data.F32[i] = initialGuess->data.F32[i];176 } else {177 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);178 }179 }180 } else {181 for (i = 0; i < initialGuess->n; i++) {182 inputParameterList->data.F32[i] = gsl_vector_get(params, i);183 }184 }185 186 // Call the user-supplied function.187 tmpf = evalModel(inputParameterList, coord);188 189 // Free allocated memory and return the value of the function.190 psFree(inputParameterList);191 return (tmpf);192 }193 194 /******************************************************************************195 p_psMinFuncDeriv(*params, *funcData): a GSL-like wrapper for the196 user-supplied procedure which calculates the derviative of the function to be197 minimized.198 199 params: The parameters of the function to be minimized. These will be200 varied by GSL in order to minimize the function.201 202 funcData: a private psLib struct which contains the data point to be203 minimized, the function and derivative function pointers, an initial204 guess at the parameters, an option parameter mask, etc.205 206 df: we calculate the derivative of the function w.r.t. to each parameter207 in "params" and return those derivatives in this psVector.208 *****************************************************************************/209 void p_psMinFuncDeriv(const gsl_vector * params, void *funcData, gsl_vector * df)210 {211 int i; // Loop index variable.212 int j; // Loop index variable.213 float tmpf; // Temporary floating point variable.214 const psVector* restrict coord = ((psMinimizeData* ) funcData)->coord;215 const psVector* restrict mask = ((psMinimizeData* ) funcData)->paramMask;216 psVector* restrict initialGuess = ((psMinimizeData* ) funcData)->initialGuess;217 psMinimizeFunctionDeriv d_evalModel = ((psMinimizeData* ) funcData)->d_evalModel;218 psVector* inputParameterList = NULL;219 220 // The GSL routines will call this function with the masked parameters221 // removed. However, the user-supplied function (to be modified) does not222 // have those parameters removed. Here will create a new parameter list223 // with the masked parameters added (we expand initialGuess).224 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);225 if (mask != NULL) {226 j = 0;227 for (i = 0; i < mask->n; i++) {228 if (mask->data.U8[i] != 0) {229 inputParameterList->data.F32[i] = initialGuess->data.F32[i];230 } else {231 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);232 }233 }234 } else {235 for (i = 0; i < initialGuess->n; i++) {236 inputParameterList->data.F32[i] = gsl_vector_get(params, i);237 }238 }239 240 // Evaluate the derivative w.r.t. each parameter.241 // NOTE: we can probably remove the calls for masked parameters.242 for (i = 0; i < initialGuess->n; i++) {243 tmpf = d_evalModel(inputParameterList, coord, i);244 gsl_vector_set(df, i, tmpf);245 }246 247 // Free allocated memory.248 psFree(inputParameterList);249 }250 251 /******************************************************************************252 Compute both p_psMinFunc and p_psMinFuncDeriv together.253 *****************************************************************************/254 void p_psMinFuncFuncDeriv(const gsl_vector * params, void *funcData, double *f, gsl_vector * df)255 {256 *f = p_psMinFunc(params, funcData);257 p_psMinFuncDeriv(params, funcData, df);258 }259 260 // The first argument to evalModel() and d_evalModel() specifies the data261 // point. It must have the same size as the second dimension of *domain.262 // The second argument must have the same size as *initialGuess and263 // *paramMask.264 265 /******************************************************************************266 p_psMinChi2Func(*x, *funcData, *outdata): We use the GSL procedure267 gsl_multifit_fdfsolver_iterate() to fit an arbitrary function, supplied by268 the user, to a set of data points. That GSL procedure requires the function269 to be fit to be in a different format than the psLib format. The purpose of270 this procedure is to serve as a GSL-format wrapper for the user-supplied271 procedure which is to be fit to the data.272 273 params: These are the parameters which are to be varied by GSL in order274 to minimize chi2 over the data set.275 276 funcData: this data structure contains the input values over which the277 function will be evaluated, the expected value of the function at278 those points, the amount of error tolerable at those points, a mask279 vector which specifies which parameters to the function are to be280 constant, and an initial guess at the parameters.281 282 outData: The function is evaluated at each point, then subtract the283 expected value and divide by the error.284 *****************************************************************************/285 int p_psMinChi2Func(const gsl_vector * params, void *funcData, gsl_vector * outData)286 {287 int i; // Loop index variable.288 int j; // Loop index variable.289 float tmpf; // Temporary floating point variable.290 const psImage* restrict domain = ((psMinChi2Data* ) funcData)->domain;291 const psVector* restrict data = ((psMinChi2Data* ) funcData)->data;292 const psVector* restrict errors = ((psMinChi2Data* ) funcData)->errors;293 const psVector* restrict mask = ((psMinChi2Data* ) funcData)->paramMask;294 psVector* restrict initialGuess = ((psMinChi2Data* ) funcData)->initialGuess;295 psMinimizeFunction evalModel = ((psMinChi2Data* ) funcData)->evalModel;296 psVector* inputParameterList = NULL;297 psVector* tmpVecPtr = NULL;298 299 tmpVecPtr = psVectorAlloc(domain->numCols, PS_TYPE_F32);300 301 // The GSL routines will call this function with the masked parameters302 // removed. However, the user-supplied function (to be modified) does not303 // have those parameters removed. Here will create a new parameter list304 // with the masked parameters added (we expand initialGuess).305 306 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);307 if (mask != NULL) {308 j = 0;309 for (i = 0; i < mask->n; i++) {310 if (mask->data.U8[i] != 0) {311 inputParameterList->data.F32[i] = initialGuess->data.F32[i];312 } else {313 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);314 }315 }316 } else {317 for (i = 0; i < initialGuess->n; i++) {318 inputParameterList->data.F32[i] = gsl_vector_get(params, i);319 }320 }321 322 // Evaluate the function at each data point.323 for (i = 0; i < domain->numRows; i++) {324 for (j = 0; j < domain->numCols; j++) {325 tmpVecPtr->data.F32[j] = domain->data.F32[i][j];326 }327 tmpf = evalModel(tmpVecPtr, inputParameterList);328 329 gsl_vector_set(outData, i, (tmpf - data->data.F32[i]) / errors->data.F32[i]);330 }331 332 // Free allocated memory.333 psFree(inputParameterList);334 psFree(tmpVecPtr);335 336 return GSL_SUCCESS;337 }338 339 /******************************************************************************340 p_psMinChi2FuncDeriv(*x, *funcData, *outdata): a GSL-like wrapper for the341 user-supplied procedure which calculates the derviative of the function to be342 minimized.343 params: These are the parameters which are to be varied by GSL in order344 to minimize chi2 over the data set.345 346 funcData: this data structure contains the input values over which the347 function will be evaluated, the expected value of the function at348 those points, the amount of error tolerable at those points, a mask349 vector which specifies which parameters to the function are to be350 constant, and an initial guess at the parameters.351 352 J: The derivative is evaluated at each point and w.r.t. each parameter353 and returned in this data structure.354 *****************************************************************************/355 int p_psMinChi2FuncDeriv(const gsl_vector * params, void *funcData, gsl_matrix * J)356 {357 const psImage* restrict domain = ((psMinChi2Data* ) funcData)->domain;358 const psVector* restrict errors = ((psMinChi2Data* ) funcData)->errors;359 const psVector* restrict mask = ((psMinChi2Data* ) funcData)->paramMask;360 psVector* restrict initialGuess = ((psMinChi2Data* ) funcData)->initialGuess;361 psVector* inputParameterList = NULL;362 psVector* tmpVecPtr = NULL;363 psMinimizeFunctionDeriv d_evalModel = ((psMinChi2Data* ) funcData)->d_evalModel;364 365 size_t i;366 int j;367 float tmpf;368 369 tmpVecPtr = psVectorAlloc(domain->numCols, PS_TYPE_F32);370 371 // The GSL routines will call this functions with the masked parameters372 // removed. However, the user-supplied function (to be modified) does not373 // have those parameters removed. Here will create a new parameter list374 // with the masked parameters added (we expand initialGuess).375 376 inputParameterList = psVectorAlloc(initialGuess->n, PS_TYPE_F32);377 if (mask != NULL) {378 j = 0;379 for (i = 0; i < mask->n; i++) {380 if (mask->data.U8[i] != 0) {381 inputParameterList->data.F32[i] = initialGuess->data.F32[i];382 } else {383 inputParameterList->data.F32[i] = gsl_vector_get(params, j++);384 }385 }386 } else {387 for (i = 0; i < initialGuess->n; i++) {388 inputParameterList->data.F32[i] = gsl_vector_get(params, i);389 }390 }391 392 // Evaluate the derivtaive at each data point, and w.r.t. each parameter.393 for (i = 0; i < domain->numRows; i++) {394 for (j = 0; j < tmpVecPtr->n; j++) {395 tmpVecPtr->data.F32[j] = domain->data.F32[i][j];396 }397 398 for (j = 0; j < inputParameterList->n; j++) {399 tmpf = d_evalModel(tmpVecPtr, inputParameterList, j);400 gsl_matrix_set(J, i, j, (tmpf / errors->data.F32[i]));401 }402 }403 404 psFree(inputParameterList);405 psFree(tmpVecPtr);406 return GSL_SUCCESS;407 }408 409 int p_psMinChi2FuncFuncDeriv(const gsl_vector * params, void *funcData, gsl_vector * f, gsl_matrix * J)410 {411 p_psMinChi2Func(params, funcData, f);412 p_psMinChi2FuncDeriv(params, funcData, J);413 414 return GSL_SUCCESS;415 }416 114 417 115 /****************************************************************************** … … 433 131 434 132 /****************************************************************************** 435 p_psBuildSums1D(x): this routine returns a psVector with "x" elements. The133 VectorNormalizeGen(): this routine returns a psVector with "x" elements. The 436 134 values of the vector will be scaled uniformly between -1.0 and 1.0. 437 *****************************************************************************/438 psVector* psBuildImageScalingFactors(int x)135 *****************************************************************************/ 136 psVector* VectorNormalizeGen(int x) 439 137 { 440 int i = 0; // loop index variable.441 psVector * imageScalingFactors= NULL;442 443 imageScalingFactors= psVectorAlloc(x, PS_TYPE_F32);138 int i = 0; 139 psVector *tmp = NULL; 140 141 tmp = psVectorAlloc(x, PS_TYPE_F32); 444 142 445 143 for (i = 0; i < x; i++) { 446 imageScalingFactors->data.F32[i] = (((float)2 * i) / ((float)x)) - 1.0; 447 } 448 449 return (imageScalingFactors); 450 } 144 tmp->data.F32[i] = (((float)2 * i) / ((float)x)) - 1.0; 145 } 146 147 return (tmp); 148 } 149 150 /*****************************************************************************/ 151 /* FUNCTION IMPLEMENTATION - PUBLIC */ 152 /*****************************************************************************/ 451 153 452 154 /****************************************************************************** 453 CURRENTLY NOT IN USE. 454 455 p_psPolyOrderCheck(A, N, *indx, *B, polyOrder,*flag) This routine checks if 456 all polyOrder-th terms in the polyOrder-th order sky background polynomial 457 defined by the coefficients in the array B[] are consistent with zero. If 458 true, then *flag is set to 1. Otherwise, *flag is set to 0. The matrix 459 inversion code in the middle of this procedure draws from Numerical Recipes 460 in C page 48. 461 Input: 462 A This is the LUD decomposition of the original matrix A. 463 N The size of the matrix (plus 1, actually, since offset 1). 464 indx misc Numerical Recipes data structure. 465 B The coefficients of the sky polynomial. 466 polyOrder The degree of the sky polynomial. 467 Output: 468 *flag Set this to 1 if we must recalculate the coefficients. 469 *****************************************************************************/ 470 void p_psPolyOrderCheck(float **A, int N, int *indx, float *B, int polyOrder, int *flag) 471 { 472 float **y = NULL; // This 2-D matrix will hold A^-1 473 float *col = NULL; // misc NumerRecipes data structure 474 float *error = NULL; // will hold the sqrt() of the 475 476 // diagonal of y[][]. 477 int i = 0; // loop-index variable 478 int j = 0; // loop-index variable 479 int numPolyTerms = 0; // The number of terms in the 480 481 // polynomial. 482 int lastTerm = 0; // The index location of the first 483 484 // n-th order term in array B[]. 485 int firstTerm = 0; // Index location of last such term. 486 487 // Allocate the necessary data structures for this procedure... 488 error = (float *)psAlloc((N + 1) * sizeof(float)); 489 col = (float *)psAlloc((N + 1) * sizeof(float)); 490 y = (float **)psAlloc((N + 1) * sizeof(float *)); 491 for (i = 1; i <= N; i++) { 492 y[i] = (float *)psAlloc((N + 1) * sizeof(float)); 493 } 494 495 // Invert the matrix A and put the result in y[][]. This code is taken 496 // from Numerical Recipes in C page 48. 497 for (j = 1; j <= N; j++) { 498 for (i = 1; i <= N; i++) { 499 col[i] = 0.0; 500 } 501 col[j] = 1.0; 502 // NOTE: substitue the LUD rotine 503 // lubksb(A, N, indx, col); 504 for (i = 1; i <= N; i++) { 505 y[i][j] = col[i]; 506 } 507 } 508 509 // Determine where the first n-th order (in this comment, n equals 510 // polyOrder) polynomial term is stored in the matrix B[], and also were 511 // the last n-order term is stored. Then we loop over all the n-order 512 // terms and check if they are consistent with zero. 513 514 numPolyTerms = (((polyOrder + 1) * (polyOrder + 2)) / 2); 515 lastTerm = numPolyTerms + 1; 516 firstTerm = lastTerm - polyOrder; 517 *flag = 1; 518 for (i = firstTerm; i <= lastTerm; i++) { 519 #ifdef DARWIN 520 error[i] = (float)sqrt(y[i][i]); 521 #else 522 523 error[i] = sqrtf(y[i][i]); 524 #endif 525 526 if (!((B[i] <= (2.0f * error[i])) && ((-2.0f * error[i]) <= B[i]))) { 527 *flag = 0; 528 } 529 } 530 531 // Free all memory allocated in this routine. 532 psFree(error); 533 psFree(col); 534 for (j = 1; j <= N; j++) { 535 psFree(y[j]); 536 } 537 psFree(y); 538 } 539 540 /*****************************************************************************/ 541 /* FUNCTION IMPLEMENTATION - PUBLIC */ 542 /*****************************************************************************/ 543 544 /****************************************************************************** 545 psMinimize(initialGuess, myFunction, myFunctionDeriv, coord, paramMask): 546 547 This routine must minimize an arbitrary function; it determines the set of 548 parameters of that function such that the ... 549 *****************************************************************************/ 550 psVector* psMinimize(psVector* restrict initialGuess, 551 psMinimizeFunction myFunction, 552 psMinimizeFunctionDeriv myFunctionDeriv, 553 const psVector* restrict coord, 554 const psVector* restrict paramMask) 555 { 556 int status; 557 int i = 0; 558 int j = 0; 559 int iter = 0; 560 gsl_multimin_function_fdf f; 561 const gsl_multimin_fdfminimizer_type *T; 562 gsl_multimin_fdfminimizer *s; 563 psMinimizeData inputData; 564 gsl_vector *x; 565 566 psTrace(".psLib.dataManip.psMinimize", 4, 567 "---- psMinimize() begin ----\n"); 568 569 PS_CHECK_NULL_VECTOR(initialGuess); 570 PS_CHECK_EMPTY_VECTOR(initialGuess); 571 PS_CHECK_NULL_VECTOR(coord); 572 PS_CHECK_EMPTY_VECTOR(coord); 573 if (paramMask != NULL) { 574 PS_CHECK_NULL_VECTOR(paramMask); 575 PS_CHECK_EMPTY_VECTOR(paramMask); 576 PS_CHECK_VECTOR_SIZE_EQUAL(initialGuess, paramMask); 577 } 578 579 inputData.initialGuess = initialGuess; 580 inputData.coord = coord; 581 inputData.paramMask = paramMask; 582 inputData.evalModel = myFunction; 583 inputData.d_evalModel = myFunctionDeriv; 584 inputData.paramCount = 0; 585 586 // If the user supplied a parameter mask, then count the number of 587 // non-masked elements. This will be used later in allocating a vector 588 // for the parameters. 589 if (paramMask != NULL) { 590 for (i = 0; i < paramMask->n; i++) { 591 if (paramMask->data.U8[i] != 0) { 592 inputData.paramCount++; 593 } 594 } 595 } else { 596 inputData.paramCount = initialGuess->n; 597 } 598 599 // The initial guess at the parameters for the function are written into 600 // the vector inputParameterList. If the paramMask is not NULL, then 601 // masked parameters are masked out. 602 x = gsl_vector_alloc(inputData.paramCount); 603 if (paramMask != NULL) { 604 j = 0; 605 for (i = 0; i < initialGuess->n; i++) { 606 if (paramMask->data.U8[i] == 0) { 607 gsl_vector_set(x, j++, initialGuess->data.F32[i]); 608 } 609 } 610 } else { 611 for (i = 0; i < initialGuess->n; i++) { 612 gsl_vector_set(x, i, initialGuess->data.F32[i]); 613 } 614 } 615 f.f = &p_psMinFunc; 616 f.df = &p_psMinFuncDeriv; 617 f.fdf = &p_psMinFuncFuncDeriv; 618 f.n = inputData.paramCount; 619 f.params = &inputData; 620 621 T = gsl_multimin_fdfminimizer_conjugate_fr; 622 s = gsl_multimin_fdfminimizer_alloc(T, inputData.paramCount); 623 gsl_multimin_fdfminimizer_set(s, &f, x, 0.01, 1e-4); 624 do { 625 iter++; 626 status = gsl_multimin_fdfminimizer_iterate(s); 627 628 if (status) 629 break; 630 631 status = gsl_multimin_test_gradient(s->gradient, 1e-3); 632 633 if (status == GSL_SUCCESS) 634 printf("Minimum found at:\n"); 635 636 } while (status == GSL_CONTINUE && iter < MAX_MINIMIZE_ITERATIONS); 637 638 // In the above steps we had removed the masked elements from the 639 // the solver. This next code blocks puts those masked elements 640 // into the solution. 641 if (paramMask != NULL) { 642 j = 0; 643 for (i = 0; i < initialGuess->n; i++) { 644 if (paramMask->data.U8[i] == 0) { 645 initialGuess->data.F32[i] = gsl_vector_get(s->x, j++); 646 } else { 647 initialGuess->data.F32[i] = initialGuess->data.F32[i]; 648 } 649 } 650 } else { 651 for (i = 0; i < initialGuess->n; i++) { 652 initialGuess->data.F32[i] = gsl_vector_get(s->x, i); 653 } 654 } 655 psTrace(".psLib.dataManip.psMinimize", 4, 656 "---- psMinimize() end ----\n"); 657 return (initialGuess); 658 } 659 660 /****************************************************************************** 661 This routine must determine the parameters of an arbitrary function 662 such that they best fit the supplied data points. 663 *****************************************************************************/ 664 psVector* psMinimizeChi2(psMinimizeFunction evalModel, 665 psMinimizeFunctionDeriv DevalModel, 666 const psImage* restrict domain, 667 const psVector* restrict data, 668 const psVector* restrict errors, 669 psVector* restrict initialGuess, 670 const psVector* restrict paramMask, 671 float *chiSq) 672 { 673 int numData = domain->numRows; // Number of data points 674 int status; // Return status for the GSL solver. 675 int i = 0; // Loop index variable. 676 int j = 0; // Loop index variable. 677 int iter = 0; // Iteration counter. 678 gsl_multifit_function_fdf f; // GSL structure that contains the 679 680 // functions/derivative to be solved. 681 double *xInit = NULL; // The initial guess at the parameters 682 683 // with masked parameters removed. 684 const gsl_multifit_fdfsolver_type *T; 685 686 // This tells GSL to use the Levenberg- 687 // Marquardt algorithm for chi2 688 // minimization. 689 gsl_multifit_fdfsolver *s; // GSL data structure. 690 psMinChi2Data inputData; 691 float chiSqOld = 0.0; 692 693 psTrace(".psLib.dataManip.psMinimizeChi2", 4, 694 "---- psMinimizeChi2() begin ----\n"); 695 696 PS_CHECK_NULL_IMAGE(domain); 697 PS_CHECK_EMPTY_IMAGE(domain); 698 PS_CHECK_NULL_VECTOR(data); 699 PS_CHECK_EMPTY_VECTOR(data); 700 PS_CHECK_NULL_VECTOR(errors); 701 PS_CHECK_EMPTY_VECTOR(errors); 702 PS_CHECK_NULL_VECTOR(initialGuess); 703 PS_CHECK_EMPTY_VECTOR(initialGuess); 704 PS_CHECK_VECTOR_SIZE_EQUAL(data, errors); 705 if (domain->numRows != data->n) { 706 psAbort(__func__, "Number of data points and data values not equal."); 707 } 708 if (paramMask != NULL) { 709 PS_CHECK_NULL_VECTOR(paramMask); 710 PS_CHECK_EMPTY_VECTOR(paramMask); 711 PS_CHECK_VECTOR_SIZE_EQUAL(initialGuess, paramMask); 712 } 713 714 inputData.n = numData; 715 inputData.paramCount = 0; 716 inputData.initialGuess = initialGuess; 717 inputData.domain = domain; 718 inputData.data = data; 719 inputData.errors = errors; 720 inputData.paramMask = paramMask; 721 inputData.evalModel = evalModel; 722 inputData.d_evalModel = DevalModel; 723 724 // If the user supplied a parameter mask, then count the number of 725 // non-masked elements. This will be used later in allocating a vector 726 // for the parameters. 727 if (paramMask != NULL) { 728 for (i = 0; i < paramMask->n; i++) { 729 if (paramMask->data.U8[i] != 0) { 730 inputData.paramCount++; 731 } 732 } 733 } else { 734 inputData.paramCount = initialGuess->n; 735 } 736 737 // The initial guess at the parameters for the function are written into 738 // the vector inputParameterList. If the paramMask is not NULL, then those 739 // parameters are masked out. 740 xInit = (double *)psAlloc(inputData.paramCount * sizeof(double)); 741 if (paramMask != NULL) { 742 j = 0; 743 for (i = 0; i < initialGuess->n; i++) { 744 if (paramMask->data.U8[i] == 0) { 745 xInit[j++] = initialGuess->data.F32[i]; 746 } 747 } 748 } else { 749 for (i = 0; i < initialGuess->n; i++) { 750 xInit[i] = initialGuess->data.F32[i]; 751 } 752 } 753 754 const gsl_rng_type *type; 755 gsl_rng *r; 756 757 gsl_rng_env_setup(); 758 759 type = gsl_rng_default; 760 r = gsl_rng_alloc(type); 761 762 // Initialize the main data structure used by the GSL solver. This will 763 // contain pointers to the function to be minimized, it's derivative 764 // function, the number of data points, the number of free parameters, 765 // and the data structures those functions use. 766 767 f.f = &p_psMinChi2Func; 768 f.df = &p_psMinChi2FuncDeriv; 769 f.fdf = &p_psMinChi2FuncFuncDeriv; 770 f.n = numData; 771 f.p = inputData.paramCount; 772 f.params = &inputData; 773 774 gsl_vector_view x = gsl_vector_view_array(xInit, inputData.paramCount); 775 776 T = gsl_multifit_fdfsolver_lmsder; 777 s = gsl_multifit_fdfsolver_alloc(T, numData, inputData.paramCount); 778 gsl_multifit_fdfsolver_set(s, &f, &x.vector); 779 *chiSq = 0.0; 780 chiSqOld = 0.0; 781 do { 782 iter++; 783 for (i = 0; i < initialGuess->n; i++) { 784 printf("Iteration %d: parameter %d is %.3f\n", iter, i, gsl_vector_get(s->x, i)); 785 } 786 // Perform an iteration of the GSL solver. 787 status = gsl_multifit_fdfsolver_iterate(s); 788 printf("gsl_multifit_fdfsolver_iterate() status is %s\n", gsl_strerror(status)); 789 for (i = 0; i < initialGuess->n; i++) { 790 printf("Iteration %d: parameter %d is %.3f\n", iter, i, gsl_vector_get(s->x, i)); 791 } 792 793 // If there was a problem, abort. 794 if (status) { 795 psAbort(__func__, "gsl_multifit_fdfsolver_iterate(%s)\n", gsl_strerror(status)); 796 } 797 // Test if the parameters changed by a small enough amount. 798 // NOTE: This wasn't working right when the parameters fit exactly. 799 // Figure out why. 800 // status = gsl_multifit_test_delta(s->dx, s->x, 1e-4, 1e-4); 801 802 // We test for convergence if chiSquared changes by less than 1.0 803 // as specified in the ADD. 804 *chiSq = gsl_blas_dnrm2(s->f); 805 printf("psMinimize.c: chiSq is %.3f\n", *chiSq); 806 if (fabs(*chiSq - chiSqOld) < 1.0) { 807 status = GSL_SUCCESS; 808 } else { 809 status = GSL_CONTINUE; 810 } 811 chiSqOld = *chiSq; 812 813 } while (status == GSL_CONTINUE && iter < MAX_LMM_ITERATIONS); 814 815 // In the above steps we had removed the masked elements from the 816 // the solver. This next code blocks puts those masked elements 817 // into the solution. 818 if (paramMask != NULL) { 819 j = 0; 820 for (i = 0; i < initialGuess->n; i++) { 821 if (paramMask->data.U8[i] == 0) { 822 initialGuess->data.F32[i] = gsl_vector_get(s->x, j++); 823 } else { 824 initialGuess->data.F32[i] = initialGuess->data.F32[i]; 825 } 826 } 827 } else { 828 for (i = 0; i < initialGuess->n; i++) { 829 initialGuess->data.F32[i] = gsl_vector_get(s->x, i); 830 } 831 } 832 833 // Calculate the chi-squared for the derived solution. 834 *chiSq = gsl_blas_dnrm2(s->f); 835 836 // Free all allocated memory 837 // NOTE: Free x. 838 gsl_multifit_fdfsolver_free(s); 839 psFree(xInit); 840 841 // Bye bye. 842 psTrace(".psLib.dataManip.psMinimizeChi2", 4, 843 "---- psMinimizeChi2() end ----\n"); 844 return (initialGuess); 845 } 846 847 /****************************************************************************** 848 This routine will take an procedure which calculates an arbitrary function 849 and it's derivative and minimize it. 850 851 XXX: Do this: 852 After checking that all entries in the paramMask are 1 or 0, when 853 forming the A matrix from alpha, try this: 854 855 A[i][i] = (1 + lambda*paramask[i]) * alpha[i][i]; 856 *****************************************************************************/ 857 bool psMinimizeLM(psMinimization *min, 858 psImage *covar, 859 psVector *params, 860 const psVector *paramMask, 861 const psArray *coords, 862 psMinimizeLMFunc func) 863 { 864 psVector *beta = psVectorAlloc(params->n, PS_TYPE_F64); 865 psVector *perm = psVectorAlloc(params->n, PS_TYPE_F64); 866 psVector *newParamsF64 = psVectorAlloc(params->n, PS_TYPE_F64); 867 psVector *newParamsF32 = psVectorAlloc(params->n, PS_TYPE_F32); 868 psVector *origParams = psVectorAlloc(params->n, PS_TYPE_F32); 869 psImage *alpha = psImageAlloc(params->n, params->n, PS_TYPE_F32); 870 psImage *A = psImageAlloc(params->n, params->n, PS_TYPE_F64); 871 psImage *aOut = psImageAlloc(params->n, params->n, PS_TYPE_F64); 872 psVector *deriv = psVectorAlloc(params->n, PS_TYPE_F32); 873 psVector *newDeriv = psVectorAlloc(params->n, PS_TYPE_F32); 874 psVector *NRparams = psVectorAlloc(params->n, PS_TYPE_F32); 875 int i; 876 int j; 877 int k; 878 float newValue; 879 float oldValue; 880 float lamda = 0.00005; 881 882 psTrace(".psLib.dataManip.psMinimizeLM", 4, 883 "---- psMinimizeLM() begin ----\n"); 884 psTrace(".psLib.dataManip.psMinimizeLM", 6, 885 "min->maxIter is %d\n", min->maxIter); 886 psTrace(".psLib.dataManip.psMinimizeLM", 6, 887 "min->tol is %f\n", min->tol); 888 889 for (i=0;i<params->n;i++) { 890 origParams->data.F32[i] = params->data.F32[i]; 891 } 892 893 min->lastDelta = HUGE; 894 min->lastDelta = 12345.0; 895 min->iter = 0; 896 897 while ((min->lastDelta > min->tol) && 898 (min->iter < min->maxIter)) { 899 psTrace(".psLib.dataManip.psMinimizeLM", 4, 900 "------------------------------------------------------\n"); 901 psTrace(".psLib.dataManip.psMinimizeLM", 4, 902 "Iteration %d. Delta is %f\n", min->iter, min->lastDelta); 903 904 min->value = func(deriv, params, coords); 905 906 for (i=0;i<params->n;i++) { 907 psTrace(".psLib.dataManip.psMinimizeLM", 6, 908 "params->data.F32[%d] is %f.\n", i, params->data.F32[i]); 909 } 910 for (i=0;i<params->n;i++) { 911 psTrace(".psLib.dataManip.psMinimizeLM", 6, 912 "deriv->data.F32[%d] is %f.\n", i, deriv->data.F32[i]); 913 } 914 psTrace(".psLib.dataManip.psMinimizeLM", 6, 915 "min->value is (%f)\n", min->value); 916 917 for (i=0;i<params->n;i++) { 918 if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) { 919 deriv->data.F32[i] = 0.0; 920 } 921 } 922 923 // Calculate the BETA vector. 924 for (i=0;i<params->n;i++) { 925 beta->data.F64[i] = (float) deriv->data.F32[i]; 926 psTrace(".psLib.dataManip.psMinimizeLM", 6, 927 "beta->data.F64[%d] is %f.\n", i, beta->data.F64[i]); 928 } 929 930 // Calculate the ALPHA matrix. 931 for (j=0;j<params->n;j++) { 932 for (k=0;k<params->n;k++) { 933 alpha->data.F32[j][k] = deriv->data.F32[k] * 934 deriv->data.F32[j]; 935 } 936 } 937 938 // Calculate the matrix A. 939 for (j=0;j<params->n;j++) { 940 for (k=0;k<params->n;k++) { 941 if (j == k) { 942 A->data.F64[j][k] = 943 (double) ((1.0 + lamda) * alpha->data.F32[j][k]); 944 } else { 945 A->data.F64[j][k] = (double) alpha->data.F32[j][k]; 946 } 947 } 948 } 949 for (j=0;j<params->n;j++) { 950 psTrace(".psLib.dataManip.psMinimizeLM", 6, "Matrix A[][]:\n"); 951 for (k=0;k<params->n;k++) { 952 psTrace(".psLib.dataManip.psMinimizeLM", 6, "%f ", A->data.F64[j][k]); 953 } 954 psTrace(".psLib.dataManip.psMinimizeLM", 6, "Matrix A[][]:\n"); 955 } 956 957 // Solve A * alpha = Beta 958 aOut = psMatrixLUD(aOut, perm, A); 959 newParamsF64 = psMatrixLUSolve(newParamsF64, aOut, beta, perm); 960 961 // Mask any masked parameters. 962 for (i=0;i<params->n;i++) { 963 if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) { 964 newParamsF64->data.F64[i] = (double) origParams->data.F32[i]; 965 } 966 newParamsF32->data.F32[i] = (float) newParamsF64->data.F64[i]; 967 968 psTrace(".psLib.dataManip.psMinimizeLM", 6, 969 "newParamsF32->data.F32[%d] is %f.\n", i, newParamsF32->data.F32[i]); 970 NRparams->data.F32[i] = params->data.F32[i] - newParamsF32->data.F32[i]; 971 } 972 973 psTrace(".psLib.dataManip.psMinimizeLM", 6, 974 "Calling func() with new parameters:\n"); 975 for (i=0;i<params->n;i++) { 976 psTrace(".psLib.dataManip.psMinimizeLM", 6, 977 "NRparams->data.F32[%d] is %f.\n", i, NRparams->data.F32[i]); 978 } 979 980 oldValue = min->value; 981 newValue = func(deriv, NRparams, coords); 982 psTrace(".psLib.dataManip.psMinimizeLM", 4, 983 "old/new values are (%f, %f)\n", oldValue, newValue); 984 for (i=0;i<params->n;i++) { 985 if ((paramMask != NULL) && (paramMask->data.U8[i] != 0)) { 986 deriv->data.F32[i] = 0.0; 987 } 988 } 989 990 if (oldValue > newValue) { 991 min->lastDelta = oldValue - newValue; 992 min->value = newValue; 993 994 // No need to check the paramMask here since we already did so 995 // before the last function evaluation. 996 for (i=0;i<params->n;i++) { 997 // params->data.F32[i] = (float) newParamsF64->data.F64[i]; 998 params->data.F32[i] = (float) NRparams->data.F32[i]; 999 } 1000 min->value = func(deriv, params, coords); 1001 1002 lamda*= 0.1; 1003 } else { 1004 lamda*= 10.0; 1005 } 1006 psTrace(".psLib.dataManip.psMinimizeLM", 4, 1007 "lamda is %f\n", lamda); 1008 min->iter++; 1009 } 1010 psFree(beta); 1011 psFree(perm); 1012 psFree(newParamsF64); 1013 psFree(newParamsF32); 1014 psFree(origParams); 1015 psFree(alpha); 1016 psFree(A); 1017 psFree(aOut); 1018 psFree(deriv); 1019 psFree(newDeriv); 1020 1021 if ((min->iter < min->maxIter) || 1022 (min->lastDelta <= min->tol)) { 1023 return(true); 1024 } 1025 1026 psTrace(".psLib.dataManip.psMinimizeLM", 4, 1027 "---- psMinimizeLM() end (false) ----\n"); 1028 return(false); 1029 } 1030 1031 /****************************************************************************** 1032 This routine will take an procedure which calculates an arbitrary function 1033 and it's derivative and minimize the chi-squared match between that function 1034 at the specified coords and the specified value at those coords. 155 psMinimizeLMChi2(): This routine will take an procedure which calculates an 156 arbitrary function and it's derivative and minimize the chi-squared match 157 between that function at the specified coords and the specified value at 158 those coords. 1035 159 1036 160 XXX: Do this: … … 1265 389 1266 390 /****************************************************************************** 1267 This routine must fit a polynomial of degree myPoly to the data points 1268 (x, y) and return the coefficients of that polynomial, as well as the 1269 error for each data point (yErr).391 psVectorFitPolynomial1D(): This routine must fit a polynomial of degree 392 myPoly to the data points (x, y) and return the coefficients of that 393 polynomial, as well as the error for each data point (yErr). 1270 394 1271 395 XXX: NOTE: yErr is currently ignored. … … 1273 397 psPolynomial1D* psVectorFitPolynomial1D(psPolynomial1D* myPoly, 1274 398 const psVector* restrict x, 1275 const psVector* restrict y, const psVector* restrict yErr) 399 const psVector* restrict y, 400 const psVector* restrict yErr) 1276 401 { 1277 402 int polyOrder = myPoly->n; … … 1381 506 1382 507 #define STEP_SIZE 0.10 1383 /******************************************************************************1384 This routine takes as input an arbitrary function, and the parameter to1385 vary. This function produces as output a bracket [a, b, c] such that1386 f(b) is less than f(a) and f(b).1387 1388 Algorithm: XXX completely ad hoc: start with the user-supplied starting1389 parameter and call that b. Calculate a/c as a fractional amount1390 smaller/larger than b. Repeat this process until a local minimum is1391 found.1392 *****************************************************************************/1393 psVector *p_psDetermineBracketOld(psVector *params,1394 int dim,1395 const psArray *coords,1396 psMinimizePowellFunc func)1397 {1398 float a = 0.0;1399 float b = 0.0;1400 float c = 0.0;1401 float fa = 0.0;1402 float fb = 0.0;1403 float fc = 0.0;1404 int iter = 100;1405 float aDir = 0.0;1406 float cDir = 0.0;1407 float new_aDir = 0.0;1408 float new_cDir = 0.0;1409 psVector *bracket = psVectorAlloc(3, PS_TYPE_F32);1410 float stepSize = params->data.F32[dim] * STEP_SIZE;1411 // float initialParam = params->data.F32[dim];1412 1413 if (stepSize == 0.0) {1414 stepSize = 1.0;1415 }1416 a = b = c = params->data.F32[0];1417 a-= stepSize;1418 c+= stepSize;1419 1420 params->data.F32[dim] = a;1421 fa = func(params, coords);1422 params->data.F32[dim] = b;1423 fb = func(params, coords);1424 params->data.F32[dim] = c;1425 fc = func(params, coords);1426 if (fa < fb) {1427 aDir = -1;1428 } else {1429 aDir = 1;1430 }1431 1432 if (fc < fb) {1433 cDir = -1;1434 } else {1435 cDir = 1;1436 }1437 1438 while (iter > 0) {1439 if ((b < a) && (b < c)) {1440 bracket->data.F32[0] = a;1441 bracket->data.F32[1] = b;1442 bracket->data.F32[2] = c;1443 return(bracket);1444 }1445 stepSize*= (1.0 + stepSize);1446 a = a - stepSize;1447 c = c + stepSize;1448 1449 params->data.F32[dim] = a;1450 fa = func(params, coords);1451 params->data.F32[dim] = c;1452 fc = func(params, coords);1453 1454 //printf("HMMM(%d): (%f %f %f) (%f %f %f)\n", iter, a, b, c, fa, fb, fc);1455 1456 if (fa < fb) {1457 new_aDir = -1;1458 } else {1459 new_aDir = 1;1460 }1461 1462 if (fc < fb) {1463 new_cDir = -1;1464 } else {1465 new_cDir = 1;1466 }1467 if ((new_aDir == 1) && (aDir == -1)) {1468 bracket->data.F32[0] = a;1469 bracket->data.F32[1] = b;1470 bracket->data.F32[2] = c;1471 return(bracket);1472 }1473 1474 if ((new_cDir == 1) && (cDir == -1)) {1475 bracket->data.F32[0] = a;1476 bracket->data.F32[1] = b;1477 bracket->data.F32[2] = c;1478 return(bracket);1479 }1480 aDir = new_aDir;1481 cDir = new_cDir;1482 iter--;1483 }1484 psFree(bracket);1485 return(NULL);1486 }1487 1488 508 /****************************************************************************** 1489 509 This routine takes as input an arbitrary function, and the parameter to … … 1666 686 } 1667 687 1668 1669 /******************************************************************************1670 This routine must minimize a possibly multi-dimensional function1671 (several parameters) along a single dimension.1672 *****************************************************************************/1673 bool psMinimize1DFunc(psMinimization *min,1674 psVector *params,1675 int dim,1676 const psArray *coords,1677 psMinimizePowellFunc func)1678 {1679 psVector *bracket;1680 float a = 0.0;1681 float b = 0.0;1682 float c = 0.0;1683 float n = 0.0;1684 float fa = 0.0;1685 float fb = 0.0;1686 float fc = 0.0;1687 float fn = 0.0;1688 // float initialParam = params->data.F32[dim];1689 1690 bracket = p_psDetermineBracketOld(params, dim, coords, func);1691 if (bracket == NULL) {1692 psAbort(__func__, "Could not bracket minimum.");1693 }1694 1695 min->iter = 0;1696 while (min->iter < min->maxIter) {1697 min->iter++;1698 //printf("psMinimize1DFunc(): iteration %d\n", min->iter);1699 a = bracket->data.F32[0];1700 b = bracket->data.F32[1];1701 c = bracket->data.F32[2];1702 1703 params->data.F32[dim] = a;1704 fa = func(params, coords);1705 params->data.F32[dim] = b;1706 fb = func(params, coords);1707 params->data.F32[dim] = c;1708 fc = func(params, coords);1709 //printf("Iteration %d: f(%f %f %f) is (%f %f %f)\n", min->iter, a, b, c, fa, fb, fc);1710 1711 // We determine which is the biggest segment in [a,b,c] then split1712 // that with the point n.1713 if ((b-a) > (c-b)) {1714 // This is the golden section formula1715 params->data.F32[dim] = n = a + (0.69 * (b-a));1716 fn = func(params, coords);1717 if (fn > fb) {1718 // a = n, b = b, c = c1719 bracket->data.F32[0] = n;1720 } else {1721 // a = a, b = n, c = b1722 bracket->data.F32[1] = n;1723 bracket->data.F32[2] = b;1724 }1725 } else {1726 params->data.F32[dim] = n = b + (0.69 * (c-b));1727 fn = func(params, coords);1728 if (fn > fb) {1729 // a = a, b = b, c = n1730 bracket->data.F32[2] = n;1731 } else {1732 // a = b, b = n, c = c1733 bracket->data.F32[0] = b;1734 bracket->data.F32[1] = n;1735 }1736 }1737 1738 if ((fabs(a-b) < min->tol) &&1739 (fabs(b-c) < min->tol)) {1740 // psFree(bracket);1741 // XXX: is this line correct?1742 params->data.F32[dim] = bracket->data.F32[1];1743 min->value = func(params, coords);1744 return(true);1745 }1746 }1747 1748 // psFree(bracket);1749 return(false);1750 }1751 688 1752 689 /****************************************************************************** … … 2168 1105 return(psMinimizePowell(min, params, paramMask, coords, myPowellChi2Func)); 2169 1106 } 1107 1108
Note:
See TracChangeset
for help on using the changeset viewer.
