Changeset 26747 for branches/eam_branches/psModules.stack.20100120/src/imcombine/pmSubtractionEquation.c
- Timestamp:
- Jan 31, 2010, 5:00:42 PM (16 years ago)
- Location:
- branches/eam_branches/psModules.stack.20100120
- Files:
-
- 2 edited
-
. (modified) (1 prop)
-
src/imcombine/pmSubtractionEquation.c (modified) (28 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/eam_branches/psModules.stack.20100120
- Property svn:mergeinfo changed
/branches/eam_branches/20091201/psModules (added) merged: 26686-26687,26693,26702-26703,26731-26735,26737,26739,26741-26743
- Property svn:mergeinfo changed
-
branches/eam_branches/psModules.stack.20100120/src/imcombine/pmSubtractionEquation.c
r26667 r26747 28 28 static bool calculateMatrixVector(psImage *matrix, // Least-squares matrix, updated 29 29 psVector *vector, // Least-squares vector, updated 30 double *norm, // Normalisation, updated 30 31 const psKernel *input, // Input image (target) 31 32 const psKernel *reference, // Reference image (convolution source) … … 36 37 const psImage *polyValues, // Spatial polynomial values 37 38 int footprint, // (Half-)Size of stamp 39 int normWindow, // Window (half-)size for normalisation measurement 38 40 const pmSubtractionEquationCalculationMode mode 39 41 ) … … 156 158 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 157 159 vector->data.F64[iIndex] = sumIC * poly[iTerm]; 158 // XXX TEST for Hermitians: do not calculate A - norm*B - \sum(k x B), 159 // instead, calculate A - \sum(k x B), with full hermitians 160 if (0 && !(mode & PM_SUBTRACTION_EQUATION_NORM)) { 160 if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) { 161 161 // subtract norm * sumRC * poly[iTerm] 162 162 psAssert (kernels->solution1, "programming error: define solution first!"); … … 174 174 double sumR = 0.0; // Sum of the reference 175 175 double sumI = 0.0; // Sum of the input 176 double normI1 = 0.0, normI2 = 0.0; // Sum of I_1 and I_2 within the normalisation window 176 177 for (int y = - footprint; y <= footprint; y++) { 177 178 for (int x = - footprint; x <= footprint; x++) { … … 181 182 double rr = PS_SQR(ref); 182 183 double one = 1.0; 184 185 if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) { 186 normI1 += ref; 187 normI2 += in; 188 } 189 183 190 if (weight) { 184 191 float wtVal = weight->kernel[y][x]; … … 204 211 } 205 212 } 213 214 *norm = normI2 / normI1; 215 206 216 if (mode & PM_SUBTRACTION_EQUATION_NORM) { 207 217 matrix->data.F64[normIndex][normIndex] = sumRR; … … 217 227 matrix->data.F64[bgIndex][normIndex] = sumR; 218 228 } 229 230 // check for any NAN values in the result, skip if found: 231 for (int iy = 0; iy < matrix->numRows; iy++) { 232 for (int ix = 0; ix < matrix->numCols; ix++) { 233 if (!isfinite(matrix->data.F64[iy][ix])) { 234 fprintf (stderr, "WARNING: NAN in matrix\n"); 235 return false; 236 } 237 } 238 } 239 for (int ix = 0; ix < vector->n; ix++) { 240 if (!isfinite(vector->data.F64[ix])) { 241 fprintf (stderr, "WARNING: NAN in vector\n"); 242 return false; 243 } 244 } 245 219 246 return true; 220 247 } … … 224 251 static bool calculateDualMatrixVector(psImage *matrix, // Least-squares matrix, updated 225 252 psVector *vector, // Least-squares vector, updated 253 double *norm, // Normalisation, updated 226 254 const psKernel *image1, // Image 1 227 255 const psKernel *image2, // Image 2 … … 232 260 const pmSubtractionKernels *kernels, // Kernels 233 261 const psImage *polyValues, // Spatial polynomial values 234 int footprint // (Half-)Size of stamp 262 int footprint, // (Half-)Size of stamp 263 int normWindow, // Window (half-)size for normalisation measurement 264 const pmSubtractionEquationCalculationMode mode 235 265 ) 236 266 { … … 272 302 } 273 303 304 305 // initialize the matrix and vector for NOP on all coeffs. we only fill in the coeffs we 306 // choose to calculate 307 psImageInit(matrix, 0.0); 308 psVectorInit(vector, 1.0); 309 for (int i = 0; i < matrix->numCols; i++) { 310 matrix->data.F64[i][i] = 1.0; 311 } 274 312 275 313 for (int i = 0; i < numKernels; i++) { … … 306 344 } 307 345 308 // Spatial variation 309 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) { 310 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) { 311 double aa = sumAA * poly2[iTerm][jTerm]; 312 double bb = sumBB * poly2[iTerm][jTerm]; 313 double ab = sumAB * poly2[iTerm][jTerm]; 314 315 matrix->data.F64[iIndex][jIndex] = aa; 316 matrix->data.F64[jIndex][iIndex] = aa; 317 318 matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb; 319 matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb; 320 321 matrix->data.F64[iIndex][jIndex + numParams] = ab; 322 matrix->data.F64[jIndex + numParams][iIndex] = ab; 346 // Spatial variation of kernel coeffs 347 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 348 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) { 349 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) { 350 double aa = sumAA * poly2[iTerm][jTerm]; 351 double bb = sumBB * poly2[iTerm][jTerm]; 352 double ab = sumAB * poly2[iTerm][jTerm]; 353 354 matrix->data.F64[iIndex][jIndex] = aa; 355 matrix->data.F64[jIndex][iIndex] = aa; 356 357 matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb; 358 matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb; 359 360 matrix->data.F64[iIndex][jIndex + numParams] = ab; 361 matrix->data.F64[jIndex + numParams][iIndex] = ab; 362 } 323 363 } 324 364 } … … 340 380 } 341 381 342 // Spatial variation 343 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) { 344 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) { 345 double ab = sumAB * poly2[iTerm][jTerm]; 346 matrix->data.F64[iIndex][jIndex + numParams] = ab; 347 matrix->data.F64[jIndex + numParams][iIndex] = ab; 382 // Spatial variation of kernel coeffs 383 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 384 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) { 385 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) { 386 double ab = sumAB * poly2[iTerm][jTerm]; 387 matrix->data.F64[iIndex][jIndex + numParams] = ab; 388 matrix->data.F64[jIndex + numParams][iIndex] = ab; 389 } 348 390 } 349 391 } … … 403 445 double bi2 = sumBI2 * poly[iTerm]; 404 446 double ai1 = sumAI1 * poly[iTerm]; 405 double a = sumA * poly[iTerm];447 double a = sumA * poly[iTerm]; 406 448 double bi1 = sumBI1 * poly[iTerm]; 407 double b = sumB * poly[iTerm]; 408 409 matrix->data.F64[iIndex][normIndex] = ai1; 410 matrix->data.F64[normIndex][iIndex] = ai1; 411 matrix->data.F64[iIndex][bgIndex] = a; 412 matrix->data.F64[bgIndex][iIndex] = a; 413 matrix->data.F64[iIndex + numParams][normIndex] = bi1; 414 matrix->data.F64[normIndex][iIndex + numParams] = bi1; 415 matrix->data.F64[iIndex + numParams][bgIndex] = b; 416 matrix->data.F64[bgIndex][iIndex + numParams] = b; 417 vector->data.F64[iIndex] = ai2; 418 vector->data.F64[iIndex + numParams] = bi2; 449 double b = sumB * poly[iTerm]; 450 451 if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) { 452 matrix->data.F64[iIndex][normIndex] = ai1; 453 matrix->data.F64[normIndex][iIndex] = ai1; 454 matrix->data.F64[iIndex + numParams][normIndex] = bi1; 455 matrix->data.F64[normIndex][iIndex + numParams] = bi1; 456 } 457 if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) { 458 matrix->data.F64[iIndex][bgIndex] = a; 459 matrix->data.F64[bgIndex][iIndex] = a; 460 matrix->data.F64[iIndex + numParams][bgIndex] = b; 461 matrix->data.F64[bgIndex][iIndex + numParams] = b; 462 } 463 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 464 vector->data.F64[iIndex] = ai2; 465 vector->data.F64[iIndex + numParams] = bi2; 466 if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) { 467 // subtract norm * sumRC * poly[iTerm] 468 psAssert (kernels->solution1, "programming error: define solution first!"); 469 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 470 double norm = fabs(kernels->solution1->data.F64[normIndex]); // Normalisation 471 vector->data.F64[iIndex] -= norm * ai1; 472 vector->data.F64[iIndex + numParams] -= norm * bi1; 473 } 474 } 419 475 } 420 476 } … … 425 481 double sumI2 = 0.0; // Sum of I_2 (for vector, background) 426 482 double sumI1I2 = 0.0; // Sum of I_1.I_2 (for vector, normalisation) 483 double normI1 = 0.0, normI2 = 0.0; // Sum of I_1 and I_2 within the normalisation window 427 484 for (int y = - footprint; y <= footprint; y++) { 428 485 for (int x = - footprint; x <= footprint; x++) { … … 433 490 double one = 1.0; 434 491 double i1i2 = i1 * i2; 492 493 if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) { 494 normI1 += i1; 495 normI2 += i2; 496 } 435 497 436 498 if (weight) { … … 457 519 } 458 520 } 459 matrix->data.F64[bgIndex][normIndex] = sumI1; 460 matrix->data.F64[normIndex][bgIndex] = sumI1; 461 matrix->data.F64[normIndex][normIndex] = sumI1I1; 462 matrix->data.F64[bgIndex][bgIndex] = sum1; 463 vector->data.F64[bgIndex] = sumI2; 464 vector->data.F64[normIndex] = sumI1I2; 521 522 *norm = normI2 / normI1; 523 524 if (mode & PM_SUBTRACTION_EQUATION_NORM) { 525 matrix->data.F64[normIndex][normIndex] = sumI1I1; 526 vector->data.F64[normIndex] = sumI1I2; 527 } 528 if (mode & PM_SUBTRACTION_EQUATION_BG) { 529 matrix->data.F64[bgIndex][bgIndex] = sum1; 530 vector->data.F64[bgIndex] = sumI2; 531 } 532 if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) { 533 matrix->data.F64[bgIndex][normIndex] = sumI1; 534 matrix->data.F64[normIndex][bgIndex] = sumI1; 535 } 536 537 // check for any NAN values in the result, skip if found: 538 for (int iy = 0; iy < matrix->numRows; iy++) { 539 for (int ix = 0; ix < matrix->numCols; ix++) { 540 if (!isfinite(matrix->data.F64[iy][ix])) { 541 fprintf (stderr, "WARNING: NAN in matrix\n"); 542 return false; 543 } 544 } 545 } 546 for (int ix = 0; ix < vector->n; ix++) { 547 if (!isfinite(vector->data.F64[ix])) { 548 fprintf (stderr, "WARNING: NAN in vector\n"); 549 return false; 550 } 551 } 552 465 553 466 554 return true; … … 469 557 #if 1 470 558 // Add in penalty term to least-squares vector 471 staticbool calculatePenalty(psImage *matrix, // Matrix to which to add in penalty term559 bool calculatePenalty(psImage *matrix, // Matrix to which to add in penalty term 472 560 psVector *vector, // Vector to which to add in penalty term 473 561 const pmSubtractionKernels *kernels, // Kernel parameters … … 482 570 int spatialOrder = kernels->spatialOrder; // Order of spatial variations 483 571 int numKernels = kernels->num; // Number of kernel components 572 int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variations 573 int numParams = numKernels * numSpatial; // Number of kernel parameters 574 575 // order is : 576 // [p_0,x_0,y_0 p_1,x_0,y_0, p_2,x_0,y_0] 577 // [p_0,x_1,y_0 p_1,x_1,y_0, p_2,x_1,y_0] 578 // [p_0,x_0,y_1 p_1,x_0,y_1, p_2,x_0,y_1] 579 // [norm] 580 // [bg] 581 // [q_0,x_0,y_0 q_1,x_0,y_0, q_2,x_0,y_0] 582 // [q_0,x_1,y_0 q_1,x_1,y_0, q_2,x_1,y_0] 583 // [q_0,x_0,y_1 q_1,x_0,y_1, q_2,x_0,y_1] 584 484 585 for (int i = 0; i < numKernels; i++) { 485 586 for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) { … … 488 589 psAssert(isfinite(penalties->data.F32[i]), "Invalid penalty"); 489 590 matrix->data.F64[index][index] += norm * penalties->data.F32[i]; 591 if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) { 592 matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties->data.F32[i]; 593 // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1) 594 // penalties scale with second moments 595 // 596 } 490 597 } 491 598 } … … 668 775 switch (kernels->mode) { 669 776 case PM_SUBTRACTION_MODE_1: 670 status = calculateMatrixVector(stamp->matrix, stamp->vector, stamp->image2, stamp->image1,777 status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image2, stamp->image1, 671 778 weight, window, stamp->convolutions1, kernels, 672 polyValues, footprint, mode);779 polyValues, footprint, stamps->normWindow, mode); 673 780 break; 674 781 case PM_SUBTRACTION_MODE_2: 675 status = calculateMatrixVector(stamp->matrix, stamp->vector, stamp->image1, stamp->image2,782 status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image1, stamp->image2, 676 783 weight, window, stamp->convolutions2, kernels, 677 polyValues, footprint, mode);784 polyValues, footprint, stamps->normWindow, mode); 678 785 break; 679 786 case PM_SUBTRACTION_MODE_DUAL: 680 status = calculateDualMatrixVector(stamp->matrix, stamp->vector,stamp->image1, stamp->image2, 787 status = calculateDualMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, 788 stamp->image1, stamp->image2, 681 789 weight, window, stamp->convolutions1, stamp->convolutions2, 682 kernels, polyValues, footprint );790 kernels, polyValues, footprint, stamps->normWindow, mode); 683 791 break; 684 792 default: … … 721 829 } 722 830 723 bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels, const pmSubtractionEquationCalculationMode mode) 831 bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels, 832 const pmSubtractionEquationCalculationMode mode) 724 833 { 725 834 PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false); … … 843 952 psVectorInit(sumVector, 0.0); 844 953 psImageInit(sumMatrix, 0.0); 845 int numStamps = 0; // Number of good stamps 846 for (int i = 0; i < stamps->num; i++) { 847 pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest 848 849 if (stamp->status == PM_SUBTRACTION_STAMP_USED) { 850 851 #ifdef TESTING 852 // XXX double-check for NAN in data: 853 for (int iy = 0; iy < stamp->matrix->numRows; iy++) { 854 for (int ix = 0; ix < stamp->matrix->numCols; ix++) { 855 if (!isfinite(stamp->matrix->data.F64[iy][ix])) { 856 fprintf (stderr, "WARNING: NAN in matrix\n"); 857 } 858 } 859 } 860 for (int ix = 0; ix < stamp->vector->n; ix++) { 861 if (!isfinite(stamp->vector->data.F64[ix])) { 862 fprintf (stderr, "WARNING: NAN in vector\n"); 863 } 864 } 865 #endif 866 867 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix); 868 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector); 869 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green"); 870 numStamps++; 871 } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) { 872 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red"); 873 } 874 } 875 876 #ifdef TESTING 877 for (int ix = 0; ix < sumVector->n; ix++) { 878 if (!isfinite(sumVector->data.F64[ix])) { 879 fprintf (stderr, "WARNING: NAN in vector\n"); 880 } 881 } 882 #endif 883 884 #if 0 885 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background 886 calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]); 887 #endif 888 889 #ifdef TESTING 890 for (int ix = 0; ix < sumVector->n; ix++) { 891 if (!isfinite(sumVector->data.F64[ix])) { 892 fprintf (stderr, "WARNING: NAN in vector\n"); 893 } 894 } 895 { 896 psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL); 897 psFitsWriteImageSimple("matrixInv.fits", inverse, NULL); 898 psFree(inverse); 899 } 900 { 901 psImage *X = psMatrixInvert(NULL, sumMatrix, NULL); 902 psImage *Xt = psMatrixTranspose(NULL, X); 903 psImage *XtX = psMatrixMultiply(NULL, Xt, X); 904 psFitsWriteImageSimple("matrixErr.fits", XtX, NULL); 905 psFree(X); 906 psFree(Xt); 907 psFree(XtX); 908 } 909 #endif 910 911 # define SVD_ANALYSIS 0 912 # define COEFF_SIG 0.0 913 # define SVD_TOL 0.0 914 915 psVector *solution = NULL; 916 psVector *solutionErr = NULL; 917 918 #ifdef TESTING 919 psFitsWriteImageSimple("A.fits", sumMatrix, NULL); 920 psVectorWriteFile ("B.dat", sumVector); 921 #endif 922 923 // Use SVD to determine the kernel coeffs (and validate) 924 if (SVD_ANALYSIS) { 925 926 // We have sumVector and sumMatrix. we are trying to solve the following equation: 927 // sumMatrix * x = sumVector. 928 929 // we can use any standard matrix inversion to solve this. However, the basis 930 // functions in general have substantial correlation, so that the solution may be 931 // somewhat poorly determined or unstable. If not numerically ill-conditioned, the 932 // system of equations may be statistically ill-conditioned. Noise in the image 933 // will drive insignificant, but correlated, terms in the solution. To avoid these 934 // problems, we can use SVD to identify numerically unconstrained values and to 935 // avoid statistically badly determined value. 936 937 // A = sumMatrix, B = sumVector 938 // SVD: A = U w V^T -> A^{-1} = V (1/w) U^T 939 // x = V (1/w) (U^T B) 940 // \sigma_x = sqrt(diag(A^{-1})) 941 // solve for x and A^{-1} to get x & dx 942 // identify the elements of (1/w) that are nan (1/0.0) -> set to 0.0 943 // identify the elements of x that are insignificant (x / dx < 1.0? < 0.5?) -> set to 0.0 944 945 // If I use the SVD trick to re-condition the matrix, I need to break out the 946 // kernel and normalization terms from the background term. 947 // XXX is this true? or was this due to an error in the analysis? 948 949 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background 950 951 // now pull out the kernel elements into their own square matrix 952 psImage *kernelMatrix = psImageAlloc (sumMatrix->numCols - 1, sumMatrix->numRows - 1, PS_TYPE_F64); 953 psVector *kernelVector = psVectorAlloc (sumMatrix->numCols - 1, PS_TYPE_F64); 954 955 for (int ix = 0, kx = 0; ix < sumMatrix->numCols; ix++) { 956 if (ix == bgIndex) continue; 957 for (int iy = 0, ky = 0; iy < sumMatrix->numRows; iy++) { 958 if (iy == bgIndex) continue; 959 kernelMatrix->data.F64[ky][kx] = sumMatrix->data.F64[iy][ix]; 960 ky++; 961 } 962 kernelVector->data.F64[kx] = sumVector->data.F64[ix]; 963 kx++; 964 } 965 966 psImage *U = NULL; 967 psImage *V = NULL; 968 psVector *w = NULL; 969 if (!psMatrixSVD (&U, &w, &V, kernelMatrix)) { 970 psError(PS_ERR_UNKNOWN, false, "failed to perform SVD on sumMatrix\n"); 971 return NULL; 972 } 973 974 // calculate A_inverse: 975 // Ainv = V * w * U^T 976 psImage *wUt = p_pmSubSolve_wUt (w, U); 977 psImage *Ainv = p_pmSubSolve_VwUt (V, wUt); 978 psImage *Xvar = NULL; 979 psFree (wUt); 980 981 # ifdef TESTING 982 // kernel terms: 983 for (int i = 0; i < w->n; i++) { 984 fprintf (stderr, "w: %f\n", w->data.F64[i]); 985 } 986 # endif 987 // loop over w adding in more and more of the values until chisquare is no longer 988 // dropping significantly. 989 // XXX this does not seem to work very well: we seem to need all terms even for 990 // simple cases... 991 992 psVector *Xsvd = NULL; 993 { 994 psVector *Ax = NULL; 995 psVector *UtB = NULL; 996 psVector *wUtB = NULL; 997 998 psVector *wApply = psVectorAlloc(w->n, PS_TYPE_F64); 999 psVector *wMask = psVectorAlloc(w->n, PS_TYPE_U8); 1000 psVectorInit (wMask, 1); // start by masking everything 1001 1002 double chiSquareLast = NAN; 1003 int maxWeight = 0; 1004 1005 double Axx, Bx, y2; 1006 1007 // XXX this is an attempt to exclude insignificant modes. 1008 // it was not successful with the ISIS kernel set: removing even 1009 // the least significant mode leaves additional ringing / noise 1010 // because the terms are so coupled. 1011 for (int k = 0; false && (k < w->n); k++) { 1012 1013 // unmask the k-th weight 1014 wMask->data.U8[k] = 0; 1015 p_pmSubSolve_SetWeights(wApply, w, wMask); 1016 1017 // solve for x: 1018 // x = V * w * (U^T * B) 1019 p_pmSubSolve_UtB (&UtB, U, kernelVector); 1020 p_pmSubSolve_wUtB (&wUtB, wApply, UtB); 1021 p_pmSubSolve_VwUtB (&Xsvd, V, wUtB); 1022 1023 // chi-square for this system of equations: 1024 // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2 1025 // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2 1026 p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd); 1027 p_pmSubSolve_VdV (&Axx, Ax, Xsvd); 1028 p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd); 1029 p_pmSubSolve_y2 (&y2, kernels, stamps); 1030 1031 // apparently, this works (compare with the brute force value below 1032 double chiSquare = Axx - 2.0*Bx + y2; 1033 double deltaChi = (k == 0) ? chiSquare : chiSquareLast - chiSquare; 1034 chiSquareLast = chiSquare; 1035 1036 // fprintf (stderr, "chi square = %f, delta: %f\n", chiSquare, deltaChi); 1037 if (k && !maxWeight && (deltaChi < 1.0)) { 1038 maxWeight = k; 1039 } 1040 } 1041 1042 // keep all terms or we get extra ringing 1043 maxWeight = w->n; 1044 psVectorInit (wMask, 1); 1045 for (int k = 0; k < maxWeight; k++) { 1046 wMask->data.U8[k] = 0; 1047 } 1048 p_pmSubSolve_SetWeights(wApply, w, wMask); 1049 1050 // solve for x: 1051 // x = V * w * (U^T * B) 1052 p_pmSubSolve_UtB (&UtB, U, kernelVector); 1053 p_pmSubSolve_wUtB (&wUtB, wApply, UtB); 1054 p_pmSubSolve_VwUtB (&Xsvd, V, wUtB); 1055 1056 // chi-square for this system of equations: 1057 // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2 1058 // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2 1059 p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd); 1060 p_pmSubSolve_VdV (&Axx, Ax, Xsvd); 1061 p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd); 1062 p_pmSubSolve_y2 (&y2, kernels, stamps); 1063 1064 // apparently, this works (compare with the brute force value below 1065 double chiSquare = Axx - 2.0*Bx + y2; 1066 psLogMsg ("psModules.imcombine", PS_LOG_INFO, "model kernel with %d terms; chi square = %f\n", maxWeight, chiSquare); 1067 1068 // re-calculate A^{-1} to get new variances: 1069 // Ainv = V * w * U^T 1070 // XXX since we keep all terms, this is identical to Ainv 1071 psImage *wUt = p_pmSubSolve_wUt (wApply, U); 1072 Xvar = p_pmSubSolve_VwUt (V, wUt); 1073 psFree (wUt); 1074 1075 psFree (Ax); 1076 psFree (UtB); 1077 psFree (wUtB); 1078 psFree (wApply); 1079 psFree (wMask); 1080 } 1081 1082 // copy the kernel solutions to the full solution vector: 1083 solution = psVectorAlloc(sumVector->n, PS_TYPE_F64); 1084 solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64); 1085 1086 for (int ix = 0, kx = 0; ix < sumVector->n; ix++) { 1087 if (ix == bgIndex) { 1088 solution->data.F64[ix] = 0; 1089 solutionErr->data.F64[ix] = 0.001; 1090 continue; 1091 } 1092 solutionErr->data.F64[ix] = sqrt(Ainv->data.F64[kx][kx]); 1093 solution->data.F64[ix] = Xsvd->data.F64[kx]; 1094 kx++; 1095 } 1096 1097 psFree (kernelMatrix); 1098 psFree (kernelVector); 1099 1100 psFree (U); 1101 psFree (V); 1102 psFree (w); 1103 1104 psFree (Ainv); 1105 psFree (Xsvd); 1106 } else { 1107 psVector *permutation = NULL; // Permutation vector, required for LU decomposition 1108 psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix); 1109 if (!luMatrix) { 1110 psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n"); 1111 psFree(solution); 1112 psFree(sumVector); 1113 psFree(sumMatrix); 1114 psFree(luMatrix); 1115 psFree(permutation); 1116 return NULL; 1117 } 1118 1119 solution = psMatrixLUSolution(NULL, luMatrix, sumVector, permutation); 1120 psFree(luMatrix); 1121 psFree(permutation); 1122 if (!solution) { 1123 psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n"); 1124 psFree(solution); 1125 psFree(sumVector); 1126 psFree(sumMatrix); 1127 return NULL; 1128 } 1129 1130 // XXX LUD does not provide A^{-1}? fake the error for now 1131 solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64); 1132 for (int ix = 0; ix < sumVector->n; ix++) { 1133 solutionErr->data.F64[ix] = 0.1*solution->data.F64[ix]; 1134 } 1135 } 1136 1137 if (!kernels->solution1) { 1138 kernels->solution1 = psVectorAlloc (sumVector->n, PS_TYPE_F64); 1139 psVectorInit (kernels->solution1, 0.0); 1140 } 1141 1142 // only update the solutions that we chose to calculate: 1143 if (mode & PM_SUBTRACTION_EQUATION_NORM) { 1144 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 1145 kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex]; 1146 } 1147 if (mode & PM_SUBTRACTION_EQUATION_BG) { 1148 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background 1149 kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex]; 1150 } 1151 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 1152 int numKernels = kernels->num; 1153 int spatialOrder = kernels->spatialOrder; // Order of spatial variation 1154 int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms 1155 for (int i = 0; i < numKernels * numPoly; i++) { 1156 // XXX fprintf (stderr, "%f +/- %f (%f) -> ", solution->data.F64[i], solutionErr->data.F64[i], fabs(solution->data.F64[i]/solutionErr->data.F64[i])); 1157 if (fabs(solution->data.F64[i] / solutionErr->data.F64[i]) < COEFF_SIG) { 1158 // XXX fprintf (stderr, "drop\n"); 1159 kernels->solution1->data.F64[i] = 0.0; 1160 } else { 1161 // XXX fprintf (stderr, "keep\n"); 1162 kernels->solution1->data.F64[i] = solution->data.F64[i]; 1163 } 1164 } 1165 } 1166 // double chiSquare = p_pmSubSolve_ChiSquare (kernels, stamps); 1167 // fprintf (stderr, "chi square Brute = %f\n", chiSquare); 1168 1169 psFree(solution); 1170 psFree(sumVector); 1171 psFree(sumMatrix); 1172 1173 #ifdef TESTING 1174 // XXX double-check for NAN in data: 1175 for (int ix = 0; ix < kernels->solution1->n; ix++) { 1176 if (!isfinite(kernels->solution1->data.F64[ix])) { 1177 fprintf (stderr, "WARNING: NAN in vector\n"); 1178 } 1179 } 1180 #endif 1181 1182 } else { 1183 // Dual convolution solution 1184 1185 // Accumulation of stamp matrices/vectors 1186 psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64); 1187 psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64); 1188 psImageInit(sumMatrix, 0.0); 1189 psVectorInit(sumVector, 0.0); 954 955 psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations 1190 956 1191 957 int numStamps = 0; // Number of good stamps … … 1195 961 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix); 1196 962 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector); 963 psVectorAppend(norms, stamp->norm); 964 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green"); 965 numStamps++; 966 } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) { 967 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red"); 968 } 969 } 970 971 #if 0 972 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background 973 calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]); 974 #endif 975 976 psVector *solution = NULL; // Solution to equation! 977 solution = psVectorAlloc(numParams, PS_TYPE_F64); 978 psVectorInit(solution, 0); 979 980 #if 0 981 // Regular, straight-forward solution 982 solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN); 983 #else 984 { 985 // Solve normalisation and background separately 986 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 987 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background 988 989 psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm 990 if (!psVectorStats(stats, norms, NULL, NULL, 0)) { 991 psError(PS_ERR_UNKNOWN, false, "Unable to determine median normalisation"); 992 psFree(stats); 993 psFree(sumMatrix); 994 psFree(sumVector); 995 psFree(norms); 996 return false; 997 } 998 999 double normValue = stats->robustMedian; 1000 // double bgValue = 0.0; 1001 1002 psFree(stats); 1003 1004 fprintf(stderr, "Norm: %lf\n", normValue); 1005 1006 // Solve kernel components 1007 for (int i = 0; i < numSolution1; i++) { 1008 sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i]; 1009 1010 sumMatrix->data.F64[i][normIndex] = 0.0; 1011 sumMatrix->data.F64[normIndex][i] = 0.0; 1012 } 1013 sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex]; 1014 sumMatrix->data.F64[bgIndex][normIndex] = 0.0; 1015 sumMatrix->data.F64[normIndex][bgIndex] = 0.0; 1016 1017 sumMatrix->data.F64[normIndex][normIndex] = 1.0; 1018 sumVector->data.F64[normIndex] = 0.0; 1019 1020 solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN); 1021 1022 solution->data.F64[normIndex] = normValue; 1023 } 1024 # endif 1025 1026 if (!kernels->solution1) { 1027 kernels->solution1 = psVectorAlloc(sumVector->n, PS_TYPE_F64); 1028 psVectorInit(kernels->solution1, 0.0); 1029 } 1030 1031 // only update the solutions that we chose to calculate: 1032 if (mode & PM_SUBTRACTION_EQUATION_NORM) { 1033 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 1034 kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex]; 1035 } 1036 if (mode & PM_SUBTRACTION_EQUATION_BG) { 1037 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background 1038 kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex]; 1039 } 1040 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 1041 int numKernels = kernels->num; 1042 int spatialOrder = kernels->spatialOrder; // Order of spatial variation 1043 int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms 1044 for (int i = 0; i < numKernels * numPoly; i++) { 1045 kernels->solution1->data.F64[i] = solution->data.F64[i]; 1046 } 1047 } 1048 1049 psFree(solution); 1050 psFree(sumVector); 1051 psFree(sumMatrix); 1052 1053 #ifdef TESTING 1054 // XXX double-check for NAN in data: 1055 for (int ix = 0; ix < kernels->solution1->n; ix++) { 1056 if (!isfinite(kernels->solution1->data.F64[ix])) { 1057 fprintf (stderr, "WARNING: NAN in vector\n"); 1058 } 1059 } 1060 #endif 1061 1062 } else { 1063 // Dual convolution solution 1064 1065 // Accumulation of stamp matrices/vectors 1066 psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64); 1067 psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64); 1068 psImageInit(sumMatrix, 0.0); 1069 psVectorInit(sumVector, 0.0); 1070 1071 psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations 1072 1073 int numStamps = 0; // Number of good stamps 1074 for (int i = 0; i < stamps->num; i++) { 1075 pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest 1076 if (stamp->status == PM_SUBTRACTION_STAMP_USED) { 1077 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix); 1078 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector); 1079 1080 psVectorAppend(norms, stamp->norm); 1197 1081 1198 1082 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green"); … … 1207 1091 1208 1092 #if 1 1209 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background 1210 calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]); 1093 // int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background 1094 // calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]); 1095 1096 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 1097 calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 1000.0); 1211 1098 #endif 1212 1099 … … 1216 1103 1217 1104 #if 0 1105 // Regular, straight-forward solution 1106 solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN); 1107 #else 1218 1108 { 1219 solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN); 1220 if (!solution) { 1221 psError(PS_ERR_UNKNOWN, false, "SVD solution of least-squares equation failed.\n"); 1109 // Solve normalisation and background separately 1110 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 1111 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background 1112 1113 #if 0 1114 psImage *normMatrix = psImageAlloc(2, 2, PS_TYPE_F64); 1115 psVector *normVector = psVectorAlloc(2, PS_TYPE_F64); 1116 1117 normMatrix->data.F64[0][0] = sumMatrix->data.F64[normIndex][normIndex]; 1118 normMatrix->data.F64[1][1] = sumMatrix->data.F64[bgIndex][bgIndex]; 1119 normMatrix->data.F64[0][1] = normMatrix->data.F64[1][0] = sumMatrix->data.F64[normIndex][bgIndex]; 1120 1121 normVector->data.F64[0] = sumVector->data.F64[normIndex]; 1122 normVector->data.F64[1] = sumVector->data.F64[bgIndex]; 1123 1124 psVector *normSolution = psMatrixSolveSVD(NULL, normMatrix, normVector, NAN); 1125 1126 double normValue = normSolution->data.F64[0]; 1127 double bgValue = normSolution->data.F64[1]; 1128 1129 psFree(normMatrix); 1130 psFree(normVector); 1131 psFree(normSolution); 1132 #endif 1133 1134 psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm 1135 if (!psVectorStats(stats, norms, NULL, NULL, 0)) { 1136 psError(PS_ERR_UNKNOWN, false, "Unable to determine median normalisation"); 1137 psFree(stats); 1222 1138 psFree(sumMatrix); 1223 1139 psFree(sumVector); 1224 return NULL; 1225 } 1226 1227 #ifdef TESTING 1228 for (int i = 0; i < numParams; i++) { 1229 fprintf(stderr, "Solution %d: %lf\n", i, solution->data.F64[i]); 1230 } 1231 #endif 1232 1233 int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations 1234 int numKernels = kernels->num; // Number of kernel basis functions 1235 1236 #define MASK_BASIS(INDEX) \ 1237 { \ 1238 for (int j = 0, index = INDEX; j < numSpatial; j++, index += numKernels) { \ 1239 for (int k = 0; k < numParams; k++) { \ 1240 sumMatrix->data.F64[index][k] = sumMatrix->data.F64[k][index] = 0.0; \ 1241 } \ 1242 sumMatrix->data.F64[index][index] = 1.0; \ 1243 sumVector->data.F64[index] = 0.0; \ 1244 } \ 1245 } 1246 1247 #define TOL 1.0e-3 1248 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 1249 double norm = fabs(solution->data.F64[normIndex]); // Normalisation 1250 double thresh = norm * TOL; // Threshold for low parameters 1251 for (int i = 0; i < numKernels; i++) { 1252 // Getting 0th order parameter value. In the presence of spatial variation, the actual value 1253 // of the parameter will vary over the image. We are in effect getting the value in the 1254 // centre of the image. If we use different polynomial functions (e.g., Chebyshev), we may 1255 // have to change this to properly determine the value of the parameter at the centre. 1256 double param1 = solution->data.F64[i], 1257 param2 = solution->data.F64[numSolution1 + i]; // Parameters of interest 1258 bool mask1 = false, mask2 = false; // Masked the parameter? 1259 if (fabs(param1) < thresh) { 1260 psTrace("psModules.imcombine", 7, "Parameter %d: 1 below threshold\n", i); 1261 MASK_BASIS(i); 1262 mask1 = true; 1263 } 1264 if (fabs(param2) < thresh) { 1265 psTrace("psModules.imcombine", 7, "Parameter %d: 2 below threshold\n", i); 1266 MASK_BASIS(numSolution1 + i); 1267 mask2 = true; 1268 } 1269 1270 if (!mask1 && !mask2) { 1271 if (fabs(param1) > fabs(param2)) { 1272 psTrace("psModules.imcombine", 7, "Parameter %d: 1 > 2\n", i); 1273 MASK_BASIS(numSolution1 + i); 1274 } else { 1275 psTrace("psModules.imcombine", 7, "Parameter %d: 2 > 1\n", i); 1276 MASK_BASIS(i); 1277 } 1278 } 1279 } 1280 1281 #ifdef TESTING 1282 psFitsWriteImageSimple ("sumMatrixFix.fits", sumMatrix, NULL); 1283 psVectorWriteFile("sumVectorFix.dat", sumVector); 1284 #endif 1285 } 1286 #endif /*** kernel-masking block ***/ 1287 solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN); 1140 psFree(norms); 1141 return false; 1142 } 1143 1144 double normValue = stats->robustMedian; 1145 1146 psFree(stats); 1147 1148 fprintf(stderr, "Norm: %lf\n", normValue); 1149 1150 // Solve kernel components 1151 for (int i = 0; i < numSolution2; i++) { 1152 sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i]; 1153 sumVector->data.F64[i + numSolution1] -= normValue * sumMatrix->data.F64[normIndex][i + numSolution1]; 1154 1155 sumMatrix->data.F64[i][normIndex] = 0.0; 1156 sumMatrix->data.F64[normIndex][i] = 0.0; 1157 1158 sumMatrix->data.F64[i + numSolution1][normIndex] = 0.0; 1159 sumMatrix->data.F64[normIndex][i + numSolution1] = 0.0; 1160 } 1161 sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex]; 1162 sumMatrix->data.F64[bgIndex][normIndex] = 0.0; 1163 sumMatrix->data.F64[normIndex][bgIndex] = 0.0; 1164 1165 sumMatrix->data.F64[normIndex][normIndex] = 1.0; 1166 1167 sumVector->data.F64[normIndex] = 0.0; 1168 1169 solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN); 1170 1171 solution->data.F64[normIndex] = normValue; 1172 } 1173 #endif 1174 1288 1175 1289 1176 #ifdef TESTING … … 1296 1183 psFree(sumVector); 1297 1184 1185 psFree(norms); 1186 1298 1187 if (!kernels->solution1) { 1299 1188 kernels->solution1 = psVectorAlloc(numSolution1, PS_TYPE_F64); 1189 psVectorInit (kernels->solution1, 0.0); 1300 1190 } 1301 1191 if (!kernels->solution2) { 1302 1192 kernels->solution2 = psVectorAlloc(numSolution2, PS_TYPE_F64); 1303 } 1193 psVectorInit (kernels->solution2, 0.0); 1194 } 1195 1196 // only update the solutions that we chose to calculate: 1197 if (mode & PM_SUBTRACTION_EQUATION_NORM) { 1198 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 1199 kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex]; 1200 } 1201 if (mode & PM_SUBTRACTION_EQUATION_BG) { 1202 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background 1203 kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex]; 1204 } 1205 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 1206 int numKernels = kernels->num; 1207 for (int i = 0; i < numKernels * numSpatial; i++) { 1208 // XXX fprintf (stderr, "keep\n"); 1209 kernels->solution1->data.F64[i] = solution->data.F64[i]; 1210 kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1]; 1211 } 1212 } 1213 1304 1214 1305 1215 memcpy(kernels->solution1->data.F64, solution->data.F64, … … 1364 1274 } 1365 1275 float sigma = sqrt(dflux2 / npix - PS_SQR(dflux1/npix)); 1276 if (!isfinite(sum)) return false; 1277 if (!isfinite(dmax)) return false; 1278 if (!isfinite(dmin)) return false; 1279 if (!isfinite(peak)) return false; 1280 1366 1281 // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dmax/peak, dmin/peak); 1367 1282 psVectorAppend(fSigRes, sigma/sum); … … 1973 1888 } 1974 1889 1890 1891 # if 0 1892 1893 #ifdef TESTING 1894 psFitsWriteImageSimple("A.fits", sumMatrix, NULL); 1895 psVectorWriteFile ("B.dat", sumVector); 1896 #endif 1897 1898 # define SVD_ANALYSIS 0 1899 # define COEFF_SIG 0.0 1900 # define SVD_TOL 0.0 1901 1902 // Use SVD to determine the kernel coeffs (and validate) 1903 if (SVD_ANALYSIS) { 1904 1905 // We have sumVector and sumMatrix. we are trying to solve the following equation: 1906 // sumMatrix * x = sumVector. 1907 1908 // we can use any standard matrix inversion to solve this. However, the basis 1909 // functions in general have substantial correlation, so that the solution may be 1910 // somewhat poorly determined or unstable. If not numerically ill-conditioned, the 1911 // system of equations may be statistically ill-conditioned. Noise in the image 1912 // will drive insignificant, but correlated, terms in the solution. To avoid these 1913 // problems, we can use SVD to identify numerically unconstrained values and to 1914 // avoid statistically badly determined value. 1915 1916 // A = sumMatrix, B = sumVector 1917 // SVD: A = U w V^T -> A^{-1} = V (1/w) U^T 1918 // x = V (1/w) (U^T B) 1919 // \sigma_x = sqrt(diag(A^{-1})) 1920 // solve for x and A^{-1} to get x & dx 1921 // identify the elements of (1/w) that are nan (1/0.0) -> set to 0.0 1922 // identify the elements of x that are insignificant (x / dx < 1.0? < 0.5?) -> set to 0.0 1923 1924 // If I use the SVD trick to re-condition the matrix, I need to break out the 1925 // kernel and normalization terms from the background term. 1926 // XXX is this true? or was this due to an error in the analysis? 1927 1928 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background 1929 1930 // now pull out the kernel elements into their own square matrix 1931 psImage *kernelMatrix = psImageAlloc (sumMatrix->numCols - 1, sumMatrix->numRows - 1, PS_TYPE_F64); 1932 psVector *kernelVector = psVectorAlloc (sumMatrix->numCols - 1, PS_TYPE_F64); 1933 1934 for (int ix = 0, kx = 0; ix < sumMatrix->numCols; ix++) { 1935 if (ix == bgIndex) continue; 1936 for (int iy = 0, ky = 0; iy < sumMatrix->numRows; iy++) { 1937 if (iy == bgIndex) continue; 1938 kernelMatrix->data.F64[ky][kx] = sumMatrix->data.F64[iy][ix]; 1939 ky++; 1940 } 1941 kernelVector->data.F64[kx] = sumVector->data.F64[ix]; 1942 kx++; 1943 } 1944 1945 psImage *U = NULL; 1946 psImage *V = NULL; 1947 psVector *w = NULL; 1948 if (!psMatrixSVD (&U, &w, &V, kernelMatrix)) { 1949 psError(PS_ERR_UNKNOWN, false, "failed to perform SVD on sumMatrix\n"); 1950 return NULL; 1951 } 1952 1953 // calculate A_inverse: 1954 // Ainv = V * w * U^T 1955 psImage *wUt = p_pmSubSolve_wUt (w, U); 1956 psImage *Ainv = p_pmSubSolve_VwUt (V, wUt); 1957 psImage *Xvar = NULL; 1958 psFree (wUt); 1959 1960 # ifdef TESTING 1961 // kernel terms: 1962 for (int i = 0; i < w->n; i++) { 1963 fprintf (stderr, "w: %f\n", w->data.F64[i]); 1964 } 1965 # endif 1966 // loop over w adding in more and more of the values until chisquare is no longer 1967 // dropping significantly. 1968 // XXX this does not seem to work very well: we seem to need all terms even for 1969 // simple cases... 1970 1971 psVector *Xsvd = NULL; 1972 { 1973 psVector *Ax = NULL; 1974 psVector *UtB = NULL; 1975 psVector *wUtB = NULL; 1976 1977 psVector *wApply = psVectorAlloc(w->n, PS_TYPE_F64); 1978 psVector *wMask = psVectorAlloc(w->n, PS_TYPE_U8); 1979 psVectorInit (wMask, 1); // start by masking everything 1980 1981 double chiSquareLast = NAN; 1982 int maxWeight = 0; 1983 1984 double Axx, Bx, y2; 1985 1986 // XXX this is an attempt to exclude insignificant modes. 1987 // it was not successful with the ISIS kernel set: removing even 1988 // the least significant mode leaves additional ringing / noise 1989 // because the terms are so coupled. 1990 for (int k = 0; false && (k < w->n); k++) { 1991 1992 // unmask the k-th weight 1993 wMask->data.U8[k] = 0; 1994 p_pmSubSolve_SetWeights(wApply, w, wMask); 1995 1996 // solve for x: 1997 // x = V * w * (U^T * B) 1998 p_pmSubSolve_UtB (&UtB, U, kernelVector); 1999 p_pmSubSolve_wUtB (&wUtB, wApply, UtB); 2000 p_pmSubSolve_VwUtB (&Xsvd, V, wUtB); 2001 2002 // chi-square for this system of equations: 2003 // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2 2004 // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2 2005 p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd); 2006 p_pmSubSolve_VdV (&Axx, Ax, Xsvd); 2007 p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd); 2008 p_pmSubSolve_y2 (&y2, kernels, stamps); 2009 2010 // apparently, this works (compare with the brute force value below 2011 double chiSquare = Axx - 2.0*Bx + y2; 2012 double deltaChi = (k == 0) ? chiSquare : chiSquareLast - chiSquare; 2013 chiSquareLast = chiSquare; 2014 2015 // fprintf (stderr, "chi square = %f, delta: %f\n", chiSquare, deltaChi); 2016 if (k && !maxWeight && (deltaChi < 1.0)) { 2017 maxWeight = k; 2018 } 2019 } 2020 2021 // keep all terms or we get extra ringing 2022 maxWeight = w->n; 2023 psVectorInit (wMask, 1); 2024 for (int k = 0; k < maxWeight; k++) { 2025 wMask->data.U8[k] = 0; 2026 } 2027 p_pmSubSolve_SetWeights(wApply, w, wMask); 2028 2029 // solve for x: 2030 // x = V * w * (U^T * B) 2031 p_pmSubSolve_UtB (&UtB, U, kernelVector); 2032 p_pmSubSolve_wUtB (&wUtB, wApply, UtB); 2033 p_pmSubSolve_VwUtB (&Xsvd, V, wUtB); 2034 2035 // chi-square for this system of equations: 2036 // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2 2037 // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2 2038 p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd); 2039 p_pmSubSolve_VdV (&Axx, Ax, Xsvd); 2040 p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd); 2041 p_pmSubSolve_y2 (&y2, kernels, stamps); 2042 2043 // apparently, this works (compare with the brute force value below 2044 double chiSquare = Axx - 2.0*Bx + y2; 2045 psLogMsg ("psModules.imcombine", PS_LOG_INFO, "model kernel with %d terms; chi square = %f\n", maxWeight, chiSquare); 2046 2047 // re-calculate A^{-1} to get new variances: 2048 // Ainv = V * w * U^T 2049 // XXX since we keep all terms, this is identical to Ainv 2050 psImage *wUt = p_pmSubSolve_wUt (wApply, U); 2051 Xvar = p_pmSubSolve_VwUt (V, wUt); 2052 psFree (wUt); 2053 2054 psFree (Ax); 2055 psFree (UtB); 2056 psFree (wUtB); 2057 psFree (wApply); 2058 psFree (wMask); 2059 } 2060 2061 // copy the kernel solutions to the full solution vector: 2062 solution = psVectorAlloc(sumVector->n, PS_TYPE_F64); 2063 solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64); 2064 2065 for (int ix = 0, kx = 0; ix < sumVector->n; ix++) { 2066 if (ix == bgIndex) { 2067 solution->data.F64[ix] = 0; 2068 solutionErr->data.F64[ix] = 0.001; 2069 continue; 2070 } 2071 solutionErr->data.F64[ix] = sqrt(Ainv->data.F64[kx][kx]); 2072 solution->data.F64[ix] = Xsvd->data.F64[kx]; 2073 kx++; 2074 } 2075 2076 psFree (kernelMatrix); 2077 psFree (kernelVector); 2078 2079 psFree (U); 2080 psFree (V); 2081 psFree (w); 2082 2083 psFree (Ainv); 2084 psFree (Xsvd); 2085 } else { 2086 psVector *permutation = NULL; // Permutation vector, required for LU decomposition 2087 psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix); 2088 if (!luMatrix) { 2089 psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n"); 2090 psFree(solution); 2091 psFree(sumVector); 2092 psFree(sumMatrix); 2093 psFree(luMatrix); 2094 psFree(permutation); 2095 return NULL; 2096 } 2097 2098 solution = psMatrixLUSolution(NULL, luMatrix, sumVector, permutation); 2099 psFree(luMatrix); 2100 psFree(permutation); 2101 if (!solution) { 2102 psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n"); 2103 psFree(solution); 2104 psFree(sumVector); 2105 psFree(sumMatrix); 2106 return NULL; 2107 } 2108 2109 // XXX LUD does not provide A^{-1}? fake the error for now 2110 solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64); 2111 for (int ix = 0; ix < sumVector->n; ix++) { 2112 solutionErr->data.F64[ix] = 0.1*solution->data.F64[ix]; 2113 } 2114 } 2115 2116 if (!kernels->solution1) { 2117 kernels->solution1 = psVectorAlloc (sumVector->n, PS_TYPE_F64); 2118 psVectorInit (kernels->solution1, 0.0); 2119 } 2120 2121 // only update the solutions that we chose to calculate: 2122 if (mode & PM_SUBTRACTION_EQUATION_NORM) { 2123 int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation 2124 kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex]; 2125 } 2126 if (mode & PM_SUBTRACTION_EQUATION_BG) { 2127 int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background 2128 kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex]; 2129 } 2130 if (mode & PM_SUBTRACTION_EQUATION_KERNELS) { 2131 int numKernels = kernels->num; 2132 int spatialOrder = kernels->spatialOrder; // Order of spatial variation 2133 int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms 2134 for (int i = 0; i < numKernels * numPoly; i++) { 2135 // XXX fprintf (stderr, "%f +/- %f (%f) -> ", solution->data.F64[i], solutionErr->data.F64[i], fabs(solution->data.F64[i]/solutionErr->data.F64[i])); 2136 if (fabs(solution->data.F64[i] / solutionErr->data.F64[i]) < COEFF_SIG) { 2137 // XXX fprintf (stderr, "drop\n"); 2138 kernels->solution1->data.F64[i] = 0.0; 2139 } else { 2140 // XXX fprintf (stderr, "keep\n"); 2141 kernels->solution1->data.F64[i] = solution->data.F64[i]; 2142 } 2143 } 2144 } 2145 // double chiSquare = p_pmSubSolve_ChiSquare (kernels, stamps); 2146 // fprintf (stderr, "chi square Brute = %f\n", chiSquare); 2147 2148 psFree(solution); 2149 psFree(sumVector); 2150 psFree(sumMatrix); 2151 # endif 2152 2153 #ifdef TESTING 2154 // XXX double-check for NAN in data: 2155 for (int iy = 0; iy < stamp->matrix->numRows; iy++) { 2156 for (int ix = 0; ix < stamp->matrix->numCols; ix++) { 2157 if (!isfinite(stamp->matrix->data.F64[iy][ix])) { 2158 fprintf (stderr, "WARNING: NAN in matrix\n"); 2159 } 2160 } 2161 } 2162 for (int ix = 0; ix < stamp->vector->n; ix++) { 2163 if (!isfinite(stamp->vector->data.F64[ix])) { 2164 fprintf (stderr, "WARNING: NAN in vector\n"); 2165 } 2166 } 2167 #endif 2168 2169 #ifdef TESTING 2170 for (int ix = 0; ix < sumVector->n; ix++) { 2171 if (!isfinite(sumVector->data.F64[ix])) { 2172 fprintf (stderr, "WARNING: NAN in vector\n"); 2173 } 2174 } 2175 #endif 2176 2177 #ifdef TESTING 2178 for (int ix = 0; ix < sumVector->n; ix++) { 2179 if (!isfinite(sumVector->data.F64[ix])) { 2180 fprintf (stderr, "WARNING: NAN in vector\n"); 2181 } 2182 } 2183 { 2184 psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL); 2185 psFitsWriteImageSimple("matrixInv.fits", inverse, NULL); 2186 psFree(inverse); 2187 } 2188 { 2189 psImage *X = psMatrixInvert(NULL, sumMatrix, NULL); 2190 psImage *Xt = psMatrixTranspose(NULL, X); 2191 psImage *XtX = psMatrixMultiply(NULL, Xt, X); 2192 psFitsWriteImageSimple("matrixErr.fits", XtX, NULL); 2193 psFree(X); 2194 psFree(Xt); 2195 psFree(XtX); 2196 } 2197 #endif 2198
Note:
See TracChangeset
for help on using the changeset viewer.
