IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Oct 25, 2010, 2:45:41 PM (16 years ago)
Author:
eugene
Message:

merge changes from eam_branches/ipp-20100823

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/imcombine/pmSubtractionEquation.c

    r29004 r29543  
    1919
    2020//#define USE_WEIGHT                      // Include weight (1/variance) in equation?
    21 //#define USE_WINDOW                      // Include weight (1/variance) in equation?
    22 
     21
     22// XXX TEST:
     23//# define USE_WINDOW                      // window to avoid neighbor contamination
     24
     25# define PENALTY false
     26# define MOMENTS (!PENALTY)
    2327
    2428//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    2731
    2832// Calculate the least-squares matrix and vector
    29 static bool calculateMatrixVector(psImage *matrix, // Least-squares matrix, updated
    30                                   psVector *vector, // Least-squares vector, updated
    31                                   double *norm,     // Normalisation, updated
     33static bool calculateMatrixVector(psImage *matrix,      // Least-squares matrix, updated
     34                                  psVector *vector,      // Least-squares vector, updated
     35                                  double normValue,      // Normalisation, supplied
    3236                                  const psKernel *input, // Input image (target)
    3337                                  const psKernel *reference, // Reference image (convolution source)
     
    3741                                  const pmSubtractionKernels *kernels, // Kernels
    3842                                  const psImage *polyValues, // Spatial polynomial values
    39                                   int footprint, // (Half-)Size of stamp
    40                                   int normWindow1, // Window (half-)size for normalisation measurement
    41                                   int normWindow2, // Window (half-)size for normalisation measurement
    42                                   const pmSubtractionEquationCalculationMode mode
     43                                  int footprint // (Half-)Size of stamp
    4344                                  )
    4445{
     
    5051
    5152    int numKernels = kernels->num;                      // Number of kernels
    52     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    53     int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    5453    int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
    5554    int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    5655    double poly[numPoly];                                 // Polynomial terms
    5756    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
     57    int numParams = numKernels * numPoly;
     58
     59    psAssert(matrix &&
     60             matrix->type.type == PS_TYPE_F64 &&
     61             matrix->numCols == numParams &&
     62             matrix->numRows == numParams,
     63             "Least-squares matrix is bad.");
     64    psAssert(vector &&
     65             vector->type.type == PS_TYPE_F64 &&
     66             vector->n == numParams,
     67             "Least-squares vector is bad.");
    5868
    5969    // Evaluate polynomial-polynomial terms
    60     // XXX we can skip this if we are not calculating kernel coeffs
    6170    for (int iyOrder = 0, iIndex = 0; iyOrder <= spatialOrder; iyOrder++) {
    6271        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex++) {
     
    8493    // [kernel 0, x^1 y^0][kernel 1 x^1 y^0]...[kernel N, x^1 y^0]
    8594    // [kernel 0, x^n y^m][kernel 1 x^n y^m]...[kernel N, x^n y^m]
    86     // normalization
    87     // bg 0, bg 1, bg 2 (only 0 is currently used?)
    8895
    8996    for (int i = 0; i < numKernels; i++) {
     
    107114
    108115            // Spatial variation of kernel coeffs
    109             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    110                 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    111                     for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    112                         double value = sumCC * poly2[iTerm][jTerm];
    113                         matrix->data.F64[iIndex][jIndex] = value;
    114                         matrix->data.F64[jIndex][iIndex] = value;
    115                     }
    116                 }
    117             }
     116            for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     117                for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     118                    double value = sumCC * poly2[iTerm][jTerm];
     119                    matrix->data.F64[iIndex][jIndex] = value;
     120                    matrix->data.F64[jIndex][iIndex] = value;
     121                }
     122            }
    118123        }
    119124
    120125        double sumRC = 0.0;             // Sum of the reference-convolution products
    121126        double sumIC = 0.0;             // Sum of the input-convolution products
    122         double sumC = 0.0;              // Sum of the convolution
    123127        for (int y = - footprint; y <= footprint; y++) {
    124128            for (int x = - footprint; x <= footprint; x++) {
     
    128132                double ic = in * conv;
    129133                double rc = ref * conv;
    130                 double c = conv;
    131134                if (weight) {
    132135                    float wtVal = weight->kernel[y][x];
    133136                    ic *= wtVal;
    134137                    rc *= wtVal;
    135                     c *= wtVal;
    136138                }
    137139                if (window) {
     
    139141                    ic *= winVal;
    140142                    rc *= winVal;
    141                     c  *= winVal;
    142143                }
    143144                sumIC += ic;
    144145                sumRC += rc;
    145                 sumC += c;
    146             }
    147         }
     146            }
     147        }
     148
    148149        // Spatial variation
    149150        for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    150             double normTerm = sumRC * poly[iTerm];
    151             double bgTerm = sumC * poly[iTerm];
    152             if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    153                 matrix->data.F64[iIndex][normIndex] = normTerm;
    154                 matrix->data.F64[normIndex][iIndex] = normTerm;
    155             }
    156             if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    157                 matrix->data.F64[iIndex][bgIndex] = bgTerm;
    158                 matrix->data.F64[bgIndex][iIndex] = bgTerm;
    159             }
    160             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    161                 vector->data.F64[iIndex] = sumIC * poly[iTerm];
    162                 if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
    163                     // subtract norm * sumRC * poly[iTerm]
    164                     psAssert (kernels->solution1, "programming error: define solution first!");
    165                     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    166                     double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
    167                     vector->data.F64[iIndex] -= norm * normTerm;
    168                 }
    169             }
    170         }
    171     }
    172 
    173     double sumRR = 0.0;                 // Sum of the reference product
    174     double sumIR = 0.0;                 // Sum of the input-reference product
    175     double sum1 = 0.0;                  // Sum of the background
    176     double sumR = 0.0;                  // Sum of the reference
    177     double sumI = 0.0;                  // Sum of the input
    178     double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    179     for (int y = - footprint; y <= footprint; y++) {
    180         for (int x = - footprint; x <= footprint; x++) {
    181             double in = input->kernel[y][x];
    182             double ref = reference->kernel[y][x];
    183             double ir = in * ref;
    184             double rr = PS_SQR(ref);
    185             double one = 1.0;
    186 
    187             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
    188                 normI1 += ref;
    189             }
    190             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
    191                 normI2 += in;
    192             }
    193 
    194             if (weight) {
    195                 float wtVal = weight->kernel[y][x];
    196                 rr *= wtVal;
    197                 ir *= wtVal;
    198                 in *= wtVal;
    199                 ref *= wtVal;
    200                 one *= wtVal;
    201             }
    202             if (window) {
    203                 float  winVal = window->kernel[y][x];
    204                 rr      *= winVal;
    205                 ir      *= winVal;
    206                 in      *= winVal;
    207                 ref *= winVal;
    208                 one *= winVal;
    209             }
    210             sumRR += rr;
    211             sumIR += ir;
    212             sumR += ref;
    213             sumI += in;
    214             sum1 += one;
    215         }
    216     }
    217 
    218     *norm = normI2 / normI1;
    219 
    220     fprintf (stderr, "normValue: %f %f %f\n", normI1, normI2, *norm);
    221 
    222     if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    223         matrix->data.F64[normIndex][normIndex] = sumRR;
    224         vector->data.F64[normIndex] = sumIR;
    225         // subtract sum over kernels * kernel solution
    226     }
    227     if (mode & PM_SUBTRACTION_EQUATION_BG) {
    228         matrix->data.F64[bgIndex][bgIndex] = sum1;
    229         vector->data.F64[bgIndex] = sumI;
    230     }
    231     if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
    232         matrix->data.F64[normIndex][bgIndex] = sumR;
    233         matrix->data.F64[bgIndex][normIndex] = sumR;
     151            vector->data.F64[iIndex] = (sumIC - normValue*sumRC) * poly[iTerm];
     152        }
    234153    }
    235154
     
    255174
    256175// Calculate the least-squares matrix and vector for dual convolution
    257 static bool calculateDualMatrixVector(psImage *matrix, // Least-squares matrix, updated
    258                                       psVector *vector, // Least-squares vector, updated
    259                                       double *norm,     // Normalisation, updated
    260                                       const psKernel *image1, // Image 1
    261                                       const psKernel *image2, // Image 2
     176// XXX we could avoid calculating these values on successive passes *if* the stamp has not changed.
     177static bool calculateDualMatrixVector(pmSubtractionStamp *stamp,              // stamp of interest
     178                                      double normValue,       // Normalisation, updated
     179                                      double normValue2,      // Normalisation, updated
    262180                                      const psKernel *weight,  // Weight image
    263181                                      const psKernel *window,  // Window image
    264                                       const psArray *convolutions1, // Convolutions of image 1 for each kernel
    265                                       const psArray *convolutions2, // Convolutions of image 2 for each kernel
    266182                                      const pmSubtractionKernels *kernels, // Kernels
    267183                                      const psImage *polyValues, // Spatial polynomial values
    268                                       int footprint, // (Half-)Size of stamp
    269                                       int normWindow1, // Window (half-)size for normalisation measurement
    270                                       int normWindow2, // Window (half-)size for normalisation measurement
    271                                       const pmSubtractionEquationCalculationMode mode
     184                                      int footprint // (Half-)Size of stamp
    272185                                      )
    273186{
    274     int numKernels = kernels->num;                      // Number of kernels
    275     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    276     int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    277     int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     187    int numKernels = kernels->num;                        // Number of kernels
     188    int spatialOrder = kernels->spatialOrder;             // Order of spatial variation
    278189    int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    279190    double poly[numPoly];                                 // Polynomial terms
    280191    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
    281192
    282     int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    283     int numParams = numKernels * numPoly + 1 + numBackground;       // Number of regular parameters
    284     int numParams2 = numKernels * numPoly;                          // Number of additional parameters for dual
    285     int numDual = numParams + numParams2;                           // Total number of parameters for dual
     193    int numParams = numKernels * numPoly;                 // Number of regular parameters
     194    int numParams2 = numKernels * numPoly;         // Number of additional parameters for dual
     195    int numDual = numParams + numParams2;          // Total number of parameters for dual
     196
     197    psImage *matrix = stamp->matrix;               // Least-squares matrix, updated
     198    psVector *vector = stamp->vector;              // Least-squares vector, updated
     199    psKernel *image1 = stamp->image1;              // Image 1
     200    psKernel *image2 = stamp->image2;              // Image 2
     201    psArray *convolutions1 = stamp->convolutions1; // Convolutions of image 1 for each kernel
     202    psArray *convolutions2 = stamp->convolutions2; // Convolutions of image 2 for each kernel
    286203
    287204    psAssert(matrix &&
     
    290207             matrix->numRows == numDual,
    291208             "Least-squares matrix is bad.");
     209
    292210    psAssert(vector &&
    293211             vector->type.type == PS_TYPE_F64 &&
     
    318236    }
    319237
     238    // the order of the elements in the matrix and vector is:
     239    // [kernel 0, x^0 y^0][kernel 1 x^0 y^0]...[kernel N, x^0 y^0]
     240    // [kernel 0, x^1 y^0][kernel 1 x^1 y^0]...[kernel N, x^1 y^0]
     241    // [kernel 0, x^n y^m][kernel 1 x^n y^m]...[kernel N, x^n y^m]
     242
     243    // for DUAL convolution analysis, we apply the normalization to I1 as follows:
     244    // norm = I2 / I1
     245    //
     246    // I1c = norm I1 + \Sum_i a_i norm I1 \cross k_i
     247    // I2c =      I2 + \Sum_i b_i      I2 \cross k_i
     248
     249    // we cannot absorb the normalization into a_i until the analysis is complete, or the
     250    // second moment terms are incorrectly calculated.
     251
    320252    for (int i = 0; i < numKernels; i++) {
    321253        psKernel *iConv1 = convolutions1->data[i]; // Convolution 1 for index i
     
    328260            double sumBB = 0.0;         // Sum of convolution products between image 2
    329261            double sumAB = 0.0;         // Sum of convolution products across images 1 and 2
     262
     263            double MxxAA = 0.0;
     264            double MyyAA = 0.0;
     265            double MxxBB = 0.0;
     266            double MyyBB = 0.0;
    330267            for (int y = - footprint; y <= footprint; y++) {
    331268                for (int x = - footprint; x <= footprint; x++) {
    332                     double aa = iConv1->kernel[y][x] * jConv1->kernel[y][x];
     269                    double aa = iConv1->kernel[y][x] * jConv1->kernel[y][x] * PS_SQR(normValue);
    333270                    double bb = iConv2->kernel[y][x] * jConv2->kernel[y][x];
    334                     double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
     271                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x] * normValue;
    335272                    if (weight) {
    336273                        float wtVal = weight->kernel[y][x];
     
    348285                    sumBB += bb;
    349286                    sumAB += ab;
    350                 }
    351             }
     287
     288                    if (MOMENTS) {
     289                        MxxAA += x*x*aa;
     290                        MyyAA += y*y*aa;
     291                        MxxBB += x*x*bb;
     292                        MyyBB += y*y*bb;
     293                    }
     294                }
     295            }
     296
     297            if (MOMENTS) {
     298                MxxAA /= stamp->normSquare1 * PS_SQR(normValue);
     299                MyyAA /= stamp->normSquare1 * PS_SQR(normValue);
     300                MxxBB /= stamp->normSquare2;
     301                MyyBB /= stamp->normSquare2;
     302            }
     303
     304            // XXX this makes the Chisq portion independent of the normalization and star flux
     305            // but may be mis-scaling between stars of different fluxes
     306            sumAA /= PS_SQR(stamp->normI2);
     307            sumAB /= PS_SQR(stamp->normI2);
     308            sumBB /= PS_SQR(stamp->normI2);
     309
     310            // fprintf (stderr, "i,j : %d %d : M(xx,yy)(AA,BB) : %f %f %f %f\n", i, j, MxxAA, MyyAA, MxxBB, MyyBB);
    352311
    353312            // Spatial variation of kernel coeffs
    354             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    355                 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    356                     for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    357                         double aa = sumAA * poly2[iTerm][jTerm];
    358                         double bb = sumBB * poly2[iTerm][jTerm];
    359                         double ab = sumAB * poly2[iTerm][jTerm];
    360 
    361                         matrix->data.F64[iIndex][jIndex] = aa;
    362                         matrix->data.F64[jIndex][iIndex] = aa;
    363 
    364                         matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb;
    365                         matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb;
    366 
    367                         matrix->data.F64[iIndex][jIndex + numParams] = ab;
    368                         matrix->data.F64[jIndex + numParams][iIndex] = ab;
    369                     }
    370                 }
    371             }
    372         }
     313            for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     314                for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     315                    double aa = sumAA * poly2[iTerm][jTerm];
     316                    double bb = sumBB * poly2[iTerm][jTerm];
     317                    double ab = sumAB * poly2[iTerm][jTerm];
     318
     319                    matrix->data.F64[iIndex][jIndex] = aa;
     320                    matrix->data.F64[jIndex][iIndex] = aa;
     321
     322                    matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb;
     323                    matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb;
     324
     325                    // add in second moments
     326                    if (MOMENTS) {
     327                        matrix->data.F64[iIndex][jIndex] += kernels->penalty * MxxAA;
     328                        matrix->data.F64[iIndex][jIndex] += kernels->penalty * MyyAA;
     329
     330                        matrix->data.F64[jIndex][iIndex] += kernels->penalty * MxxAA;
     331                        matrix->data.F64[jIndex][iIndex] += kernels->penalty * MyyAA;
     332
     333                        matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * MxxBB;
     334                        matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * MyyBB;
     335
     336                        matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * MxxBB;
     337                        matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * MyyBB;
     338
     339                        // XXX this uses the single moments, first try
     340                        // matrix->data.F64[iIndex][jIndex] += kernels->penalty * stamp->MxxI1->data.F32[i]*stamp->MxxI1->data.F32[j];
     341                        // matrix->data.F64[iIndex][jIndex] += kernels->penalty * stamp->MyyI1->data.F32[i]*stamp->MyyI1->data.F32[j];
     342                        //
     343                        // matrix->data.F64[jIndex][iIndex] += kernels->penalty * stamp->MxxI1->data.F32[i]*stamp->MxxI1->data.F32[j];
     344                        // matrix->data.F64[jIndex][iIndex] += kernels->penalty * stamp->MyyI1->data.F32[i]*stamp->MyyI1->data.F32[j];
     345                        //
     346                        // matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * stamp->MxxI2->data.F32[i]*stamp->MxxI2->data.F32[j];
     347                        // matrix->data.F64[iIndex + numParams][jIndex + numParams] += kernels->penalty * stamp->MyyI2->data.F32[i]*stamp->MyyI2->data.F32[j];
     348                        //
     349                        // matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * stamp->MxxI2->data.F32[i]*stamp->MxxI2->data.F32[j];
     350                        // matrix->data.F64[jIndex + numParams][iIndex + numParams] += kernels->penalty * stamp->MyyI2->data.F32[i]*stamp->MyyI2->data.F32[j];
     351                    }
     352                    matrix->data.F64[iIndex][jIndex + numParams] = ab;
     353                    matrix->data.F64[jIndex + numParams][iIndex] = ab;
     354                }
     355            }
     356        }
     357
     358        // we need to calculate the lower-diagonal AB elements since they are not symmetric for A <-> B
    373359        for (int j = 0; j < i; j++) {
    374360            psKernel *jConv2 = convolutions2->data[j]; // Convolution 2 for index j
     
    376362            for (int y = - footprint; y <= footprint; y++) {
    377363                for (int x = - footprint; x <= footprint; x++) {
    378                     double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
     364                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x] * normValue;
    379365                    if (weight) {
    380366                        ab *= weight->kernel[y][x];
     
    387373            }
    388374
     375            // XXX this makes the Chisq portion independent of the normalization and star flux
     376            // but may be mis-scaling between stars of different fluxes
     377            sumAB /= PS_SQR(stamp->normI2);
     378
    389379            // Spatial variation of kernel coeffs
    390             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    391                 for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    392                     for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    393                         double ab = sumAB * poly2[iTerm][jTerm];
    394                         matrix->data.F64[iIndex][jIndex + numParams] = ab;
    395                         matrix->data.F64[jIndex + numParams][iIndex] = ab;
    396                     }
    397                 }
     380            for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     381                for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     382                    double ab = sumAB * poly2[iTerm][jTerm];
     383                    matrix->data.F64[iIndex][jIndex + numParams] = ab;
     384                    matrix->data.F64[jIndex + numParams][iIndex] = ab;
     385                }
    398386            }
    399387        }
     
    402390        double sumBI2 = 0.0;            // Sum of B.I_2 products (for vector)
    403391        double sumAI1 = 0.0;            // Sum of A.I_1 products (for matrix, normalisation)
    404         double sumA = 0.0;              // Sum of A (for matrix, background)
    405392        double sumBI1 = 0.0;            // Sum of B.I_1 products (for matrix, normalisation)
    406         double sumB = 0.0;              // Sum of B products (for matrix, background)
    407         double sumI2 = 0.0;             // Sum of I_2 (for vector, background)
     393
     394        double MxxAI1 = 0.0;
     395        double MyyAI1 = 0.0;
     396        double MxxBI2 = 0.0;
     397        double MyyBI2 = 0.0;
    408398        for (int y = - footprint; y <= footprint; y++) {
    409399            for (int x = - footprint; x <= footprint; x++) {
     
    413403                float i2 = image2->kernel[y][x];
    414404
    415                 double ai2 = a * i2;
     405                double ai2 = a * i2 * normValue;
    416406                double bi2 = b * i2;
    417                 double ai1 = a * i1;
    418                 double bi1 = b * i1;
     407                double ai1 = a * i1 * PS_SQR(normValue);
     408                double bi1 = b * i1 * normValue;
    419409
    420410                if (weight) {
     
    424414                    ai1 *= wtVal;
    425415                    bi1 *= wtVal;
    426                     a *= wtVal;
    427                     b *= wtVal;
    428                     i2 *= wtVal;
    429416                }
    430417                if (window) {
     
    434421                    ai1 *= wtVal;
    435422                    bi1 *= wtVal;
    436                     a *= wtVal;
    437                     b *= wtVal;
    438                     i2 *= wtVal;
    439423                }
    440424                sumAI2 += ai2;
    441425                sumBI2 += bi2;
    442426                sumAI1 += ai1;
    443                 sumA += a;
    444427                sumBI1 += bi1;
    445                 sumB += b;
    446                 sumI2 += i2;
    447             }
    448         }
     428
     429                if (MOMENTS) {
     430                    MxxAI1 += x*x*ai1;
     431                    MyyAI1 += y*y*ai1;
     432                    MxxBI2 += x*x*bi2;
     433                    MyyBI2 += y*y*bi2;
     434                }
     435            }
     436        }
     437
     438        if (MOMENTS) {
     439            MxxAI1 /= stamp->normSquare1 * PS_SQR(normValue);
     440            MyyAI1 /= stamp->normSquare1 * PS_SQR(normValue);
     441            MxxBI2 /= stamp->normSquare2;
     442            MyyBI2 /= stamp->normSquare2;
     443        }
     444
     445        // fprintf (stderr, "i : %d : M(xx,yy)(AI1,BI2) : %f %f %f %f\n", i, MxxAI1, MyyAI1, MxxBI2, MyyBI2);
     446
     447        // XXX this makes the Chisq portion independent of the normalization and star flux
     448        // but may be mis-scaling between stars of different fluxes
     449        sumAI1 /= PS_SQR(stamp->normI2);
     450        sumBI1 /= PS_SQR(stamp->normI2);
     451        sumAI2 /= PS_SQR(stamp->normI2);
     452        sumBI2 /= PS_SQR(stamp->normI2);
     453
    449454        // Spatial variation
    450455        for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     
    452457            double bi2 = sumBI2 * poly[iTerm];
    453458            double ai1 = sumAI1 * poly[iTerm];
    454             double a   = sumA * poly[iTerm];
    455459            double bi1 = sumBI1 * poly[iTerm];
    456             double b   = sumB * poly[iTerm];
    457 
    458             if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    459                 matrix->data.F64[iIndex][normIndex] = ai1;
    460                 matrix->data.F64[normIndex][iIndex] = ai1;
    461                 matrix->data.F64[iIndex + numParams][normIndex] = bi1;
    462                 matrix->data.F64[normIndex][iIndex + numParams] = bi1;
    463             }
    464             if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
    465                 matrix->data.F64[iIndex][bgIndex] = a;
    466                 matrix->data.F64[bgIndex][iIndex] = a;
    467                 matrix->data.F64[iIndex + numParams][bgIndex] = b;
    468                 matrix->data.F64[bgIndex][iIndex + numParams] = b;
    469             }
    470             if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    471                 vector->data.F64[iIndex] = ai2;
    472                 vector->data.F64[iIndex + numParams] = bi2;
    473                 if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
    474                     // subtract norm * sumRC * poly[iTerm]
    475                     psAssert (kernels->solution1, "programming error: define solution first!");
    476                     int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    477                     double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
    478                     vector->data.F64[iIndex] -= norm * ai1;
    479                     vector->data.F64[iIndex + numParams] -= norm * bi1;
    480                 }
    481             }
    482         }
    483     }
    484 
    485     double sumI1 = 0.0;                 // Sum of I_1 (for matrix, background-normalisation)
    486     double sumI1I1 = 0.0;               // Sum of I_1^2 (for matrix, normalisation-normalisation)
    487     double sum1 = 0.0;                  // Sum of 1 (for matrix, background-background)
    488     double sumI2 = 0.0;                 // Sum of I_2 (for vector, background)
    489     double sumI1I2 = 0.0;               // Sum of I_1.I_2 (for vector, normalisation)
    490     double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    491     for (int y = - footprint; y <= footprint; y++) {
    492         for (int x = - footprint; x <= footprint; x++) {
    493             double i1 = image1->kernel[y][x];
    494             double i2 = image2->kernel[y][x];
    495 
    496             double i1i1 = i1 * i1;
    497             double one = 1.0;
    498             double i1i2 = i1 * i2;
    499 
    500             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
    501                 normI1 += i1;
    502             }
    503             if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
    504                 normI2 += i2;
    505             }
    506 
    507             if (weight) {
    508                 float wtVal = weight->kernel[y][x];
    509                 i1 *= wtVal;
    510                 i1i1 *= wtVal;
    511                 one *= wtVal;
    512                 i2 *= wtVal;
    513                 i1i2 *= wtVal;
    514             }
    515             if (window) {
    516                 float wtVal = window->kernel[y][x];
    517                 i1 *= wtVal;
    518                 i1i1 *= wtVal;
    519                 one *= wtVal;
    520                 i2 *= wtVal;
    521                 i1i2 *= wtVal;
    522             }
    523             sumI1 += i1;
    524             sumI1I1 += i1i1;
    525             sum1 += one;
    526             sumI2 += i2;
    527             sumI1I2 += i1i2;
    528         }
    529     }
    530 
    531     *norm = normI2 / normI1;
    532     fprintf (stderr, "normValue: %f %f %f\n", normI1, normI2, *norm);
    533 
    534     if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    535         matrix->data.F64[normIndex][normIndex] = sumI1I1;
    536         vector->data.F64[normIndex] = sumI1I2;
    537     }
    538     if (mode & PM_SUBTRACTION_EQUATION_BG) {
    539         matrix->data.F64[bgIndex][bgIndex] = sum1;
    540         vector->data.F64[bgIndex] = sumI2;
    541     }
    542     if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
    543         matrix->data.F64[bgIndex][normIndex] = sumI1;
    544         matrix->data.F64[normIndex][bgIndex] = sumI1;
     460            vector->data.F64[iIndex]             = ai2 - ai1;
     461            vector->data.F64[iIndex + numParams] = bi2 - bi1;
     462
     463            // fprintf (stderr, "i : %d : V(I1,I2) : %f %f\n", i, vector->data.F64[iIndex], vector->data.F64[iIndex + numParams]);
     464
     465            // add in second moments
     466            if (MOMENTS) {
     467                vector->data.F64[iIndex]             -= kernels->penalty * MxxAI1;
     468                vector->data.F64[iIndex]             -= kernels->penalty * MyyAI1;
     469
     470                vector->data.F64[iIndex + numParams] -= kernels->penalty * MxxBI2;
     471                vector->data.F64[iIndex + numParams] -= kernels->penalty * MyyBI2;
     472            }
     473        }
    545474    }
    546475
     
    565494}
    566495
    567 #if 1
    568496// Add in penalty term to least-squares vector
    569497bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
    570498                      psVector *vector,                    // Vector to which to add in penalty term
    571499                      const pmSubtractionKernels *kernels, // Kernel parameters
    572                       float norm                           // Normalisation
     500                      float normSquare1,                   // Normalisation for image 1
     501                      float normSquare2            // Normalisation for image 2
    573502  )
    574503{
     504    psAssert (kernels->mode == PM_SUBTRACTION_MODE_DUAL, "only use penalties for dual convolution");
     505
    575506    if (kernels->penalty == 0.0) {
    576507        return true;
     
    589520    // [p_0,x_1,y_0 p_1,x_1,y_0, p_2,x_1,y_0]
    590521    // [p_0,x_0,y_1 p_1,x_0,y_1, p_2,x_0,y_1]
    591     // [norm]
    592     // [bg]
     522
    593523    // [q_0,x_0,y_0 q_1,x_0,y_0, q_2,x_0,y_0]
    594524    // [q_0,x_1,y_0 q_1,x_1,y_0, q_2,x_1,y_0]
     
    600530                // Contribution to chi^2: a_i^2 P_i
    601531                psAssert(isfinite(penalties1->data.F32[i]), "Invalid penalty");
    602                 fprintf (stderr, "penalty: %f + %f (%f * %f)\n", matrix->data.F64[index][index], norm * penalties1->data.F32[i], norm, penalties1->data.F32[i]);
    603                 matrix->data.F64[index][index] += norm * penalties1->data.F32[i];
    604                 if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    605                     fprintf (stderr, "penalty: (x^%d y^%d fwhm %f) : %f + %f (%f * %f)\n", kernels->u->data.S32[index], kernels->v->data.S32[index], kernels->widths->data.F32[index],
    606                              matrix->data.F64[index + numParams + 2][index + numParams + 2], norm * penalties2->data.F32[i], norm, penalties2->data.F32[i]);
    607                     matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties2->data.F32[i];                       
    608                     // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
    609                     // penalties scale with second moments
    610                     //
    611                 }
    612             }
    613         }
    614     }
    615 
    616     return true;
    617 }
    618 # endif
     532                // fprintf (stderr, "penalty: %f + %f (%f * %f)\n", matrix->data.F64[index][index], normSquare1 * penalties1->data.F32[i], normSquare1, penalties1->data.F32[i]);
     533                matrix->data.F64[index][index] += normSquare1 * penalties1->data.F32[i];
     534
     535                // fprintf (stderr, "penalty: (x^%d y^%d fwhm %f) : %f + %f (%f * %f)\n", kernels->u->data.S32[index], kernels->v->data.S32[index], kernels->widths->data.F32[index],
     536                // matrix->data.F64[index + numParams][index + numParams], normSquare2 * penalties2->data.F32[i], normSquare2, penalties2->data.F32[i]);
     537                matrix->data.F64[index + numParams][index + numParams] += normSquare2 * penalties2->data.F32[i];                             
     538                // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
     539                // penalties scale with second moments
     540                //
     541            }
     542        }
     543    }
     544
     545    return true;
     546}
    619547
    620548//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    647575                                    int index, bool wantDual)
    648576{
    649 #if 0
    650577    // This is probably in a tight loop, so don't check inputs
    651     PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
    652     PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
    653     PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
    654     PS_ASSERT_INT_POSITIVE(index, NAN);
    655 #endif
    656 
    657578    psVector *solution = wantDual ? kernels->solution2 : kernels->solution1; // Solution vector
    658579    return p_pmSubtractionCalculatePolynomial(solution, polyValues, kernels->spatialOrder, index,
     
    662583double p_pmSubtractionSolutionNorm(const pmSubtractionKernels *kernels)
    663584{
    664 #if 0
    665585    // This is probably in a tight loop, so don't check inputs
    666     PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
    667     PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
    668     PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
    669 #endif
    670 
    671586    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    672587    return kernels->solution1->data.F64[normIndex];
     
    676591                                                const psImage *polyValues)
    677592{
    678 #if 0
    679593    // This is probably in a tight loop, so don't check inputs
    680     PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, NAN);
    681     PM_ASSERT_SUBTRACTION_KERNELS_SOLUTION(kernels, NAN);
    682     PS_ASSERT_IMAGE_NON_NULL(polyValues, NAN);
    683 #endif
    684 
    685594    int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    686595    return p_pmSubtractionCalculatePolynomial(kernels->solution1, polyValues, kernels->bgOrder, bgIndex, 1);
     
    690599// Public functions
    691600//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     601
     602bool pmSubtractionCalculateMoments(
     603    pmSubtractionKernels *kernels, // Kernels
     604    pmSubtractionStampList *stamps)
     605{
     606    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     607
     608    // XXX skip this, right?
     609    return true;
     610
     611    // these are only used by DUAL mode
     612    if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) return true;
     613
     614    psTimerStart("pmSubtractionCalculateMoments");
     615
     616    int footprint = stamps->footprint;  // Half-size of stamps
     617
     618    // Loop over each stamp and calculate its normalization factor
     619    for (int i = 0; i < stamps->num; i++) {
     620        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     621        if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) continue;
     622        if (stamp->status == PM_SUBTRACTION_STAMP_NONE) continue;
     623
     624        pmSubtractionCalculateMomentsStamp(kernels, stamp, footprint, stamps->normWindow2, stamps->normWindow1);
     625    }
     626
     627    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate moments: %f sec", psTimerClear("pmSubtractionCalculateMoments"));
     628
     629    return true;
     630}
     631
     632bool pmSubtractionCalculateMomentsStamp(
     633    pmSubtractionKernels *kernels, // Kernels
     634    pmSubtractionStamp *stamp,          // stamp on which to save normalization)
     635    int footprint,                      // (Half-)Size of stamp
     636    int normWindow1,                    // Window (half-)size for normalisation measurement
     637    int normWindow2                     // Window (half-)size for normalisation measurement
     638    )
     639{
     640    double Mxx, Myy;
     641
     642    int numKernels = kernels->num;
     643
     644    // Generate convolutions: these are generated once and saved
     645    if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
     646        psError(psErrorCodeLast(), false, "Unable to convolve stamp");
     647        return false;
     648    }
     649
     650    if (!stamp->MxxI1) {
     651        stamp->MxxI1 = psVectorAlloc (numKernels, PS_TYPE_F32);
     652    }
     653    if (!stamp->MyyI1) {
     654        stamp->MyyI1 = psVectorAlloc (numKernels, PS_TYPE_F32);
     655    }
     656    if (!stamp->MxxI2) {
     657        stamp->MxxI2 = psVectorAlloc (numKernels, PS_TYPE_F32);
     658    }
     659    if (!stamp->MyyI2) {
     660        stamp->MyyI2 = psVectorAlloc (numKernels, PS_TYPE_F32);
     661    }
     662
     663    for (int i = 0; i < numKernels; i++) {
     664        pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->convolutions1->data[i], footprint, normWindow1);
     665        stamp->MxxI1->data.F32[i] = Mxx / stamp->normI1;
     666        stamp->MyyI1->data.F32[i] = Myy / stamp->normI1;
     667        pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->convolutions2->data[i], footprint, normWindow2);
     668        stamp->MxxI2->data.F32[i] = Mxx / stamp->normI2;
     669        stamp->MyyI2->data.F32[i] = Myy / stamp->normI2;
     670    }
     671
     672    pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->image1, footprint, normWindow1);
     673    stamp->MxxI1raw = Mxx / stamp->normI1;
     674    stamp->MyyI1raw = Myy / stamp->normI1;
     675
     676    pmSubtractionCalculateMomentsKernel(&Mxx, &Myy, stamp->image2, footprint, normWindow2);
     677    stamp->MxxI2raw = Mxx / stamp->normI2;
     678    stamp->MyyI2raw = Myy / stamp->normI2;
     679
     680    // fprintf (stderr, "Mxx I1: %f, Myy I1: %f, Mxx I2: %f, Myy I2: %f\n", stamp->MxxI1raw, stamp->MyyI1raw, stamp->MxxI2raw, stamp->MyyI2raw);
     681
     682    return true;
     683}
     684
     685bool pmSubtractionCalculateMomentsKernel(double *Mxx, double *Myy, psKernel *image, int footprint, int window) {
     686
     687    double Sxx = 0.0;
     688    double Syy = 0.0;
     689    for (int y = - footprint; y <= footprint; y++) {
     690        for (int x = - footprint; x <= footprint; x++) {
     691            if (PS_SQR(x) + PS_SQR(y) > PS_SQR(window)) continue;
     692
     693            double flux = image->kernel[y][x];
     694
     695            Sxx += PS_SQR(x) * flux;
     696            Syy += PS_SQR(y) * flux;
     697        }
     698    }
     699    *Mxx = Sxx;
     700    *Myy = Syy;
     701    return true;
     702}
     703
     704///---------
     705
     706bool pmSubtractionCalculateNormalization(
     707    pmSubtractionStampList *stamps,
     708    const pmSubtractionMode mode)
     709{
     710    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     711
     712    psTimerStart("pmSubtractionCalculateNormalization");
     713
     714    psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
     715    psVector *norm2 = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
     716
     717    int footprint = stamps->footprint;  // Half-size of stamps
     718
     719    // Loop over each stamp and calculate its normalization factor
     720    for (int i = 0; i < stamps->num; i++) {
     721        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     722        if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) continue;
     723        if (stamp->status == PM_SUBTRACTION_STAMP_NONE) continue;
     724
     725        // XXX skip this if we have already calculated it? (stamp->norm does not change, just the median statistic)
     726        // XXX maybe not: the star may have changed for a given stamp -- only if norm is reset to NAN can we do this
     727        if (mode == PM_SUBTRACTION_MODE_2) {
     728            pmSubtractionCalculateNormalizationStamp(stamp, stamp->image2, stamp->image1, footprint, stamps->normWindow2, stamps->normWindow1);
     729        } else {
     730            pmSubtractionCalculateNormalizationStamp(stamp, stamp->image1, stamp->image2, footprint, stamps->normWindow1, stamps->normWindow2);
     731        }
     732        psVectorAppend(norms, stamp->norm);
     733        psVectorAppend(norm2, stamp->normSquare2 / stamp->normSquare1);
     734    }
     735
     736    psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     737    if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     738        psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
     739        psFree(stats);
     740        psFree(norms);
     741        psFree(norm2);
     742        return false;
     743    }
     744    stamps->normValue = stats->robustMedian;
     745
     746    psStatsInit(stats);
     747    if (!psVectorStats(stats, norm2, NULL, NULL, 0)) {
     748        psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
     749        psFree(stats);
     750        psFree(norms);
     751        psFree(norm2);
     752        return false;
     753    }
     754    stamps->normValue2 = stats->robustMedian;
     755
     756    psLogMsg ("psModules.imcombine", PS_LOG_INFO, "norm (1): %f (%f)\n", stamps->normValue, stamps->normValue2);
     757
     758    psFree(stats);
     759    psFree(norms);
     760    psFree(norm2);
     761
     762    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate normalization: %f sec", psTimerClear("pmSubtractionCalculateNormalization"));
     763
     764    return true;
     765}
     766
     767bool pmSubtractionCalculateNormalizationStamp(
     768    pmSubtractionStamp *stamp,          // stamp on which to save normalization)
     769    const psKernel *image1,             // Input image (target)
     770    const psKernel *image2,             // Reference image (convolution source)
     771    int footprint,                      // (Half-)Size of stamp
     772    int normWindow1,                    // Window (half-)size for normalisation measurement
     773    int normWindow2                     // Window (half-)size for normalisation measurement
     774    )
     775{
     776    double normI1 = 0.0;  // Sum of I_1 within the normalisation window (aperture)
     777    double normI2 = 0.0;  // Sum of I_2 within the normalisation window (aperture)
     778    double normSquare1 = 0.0;  // Sum of (I_1)^2 within the normalisation window (aperture)
     779    double normSquare2 = 0.0;  // Sum of (I_2)^2 within the normalisation window (aperture)
     780    for (int y = - footprint; y <= footprint; y++) {
     781        for (int x = - footprint; x <= footprint; x++) {
     782            double im1 = image1->kernel[y][x];
     783            double im2 = image2->kernel[y][x];
     784
     785            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow1)) {
     786                normI1 += im1;
     787                normSquare1 += PS_SQR(im1);
     788            }
     789            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow2)) {
     790                normI2 += im2;
     791                normSquare2 += PS_SQR(im2);
     792            }
     793        }
     794    }
     795    stamp->norm = normI2 / normI1;
     796    stamp->normI1 = normI1;
     797    stamp->normI2 = normI2;
     798    stamp->normSquare1 = normSquare1;
     799    stamp->normSquare2 = normSquare2;
     800
     801    psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
     802
     803    return true;
     804}
    692805
    693806bool pmSubtractionCalculateEquationThread(psThreadJob *job)
     
    715828    int numKernels = kernels->num;      // Number of kernel basis functions
    716829    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variations
    717     int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    718830
    719831    // numKernels is the number of unique kernel images (one for each Gaussian modified by a specific polynomial).
     
    722834    // Total number of parameters to solve for: coefficient of each kernel basis function, multipled by the
    723835    // number of coefficients for the spatial polynomial, normalisation and a constant background offset.
    724     int numParams = numKernels * numSpatial + 1 + numBackground;
     836    // XXX we no longer solve for the normalization and background in the matrix inversion
     837    int numParams = numKernels * numSpatial;
    725838    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    726839        // An additional image is convolved
     
    790903    switch (kernels->mode) {
    791904      case PM_SUBTRACTION_MODE_1:
    792         status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image2, stamp->image1,
    793                                        weight, window, stamp->convolutions1, kernels,
    794                                        polyValues, footprint, stamps->normWindow1, stamps->normWindow2, mode);
     905        status = calculateMatrixVector(stamp->matrix, stamp->vector, stamps->normValue, stamp->image2, stamp->image1,
     906                                       weight, window, stamp->convolutions1, kernels, polyValues, footprint);
    795907        break;
    796908      case PM_SUBTRACTION_MODE_2:
    797         status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image1, stamp->image2,
    798                                        weight, window, stamp->convolutions2, kernels,
    799                                        polyValues, footprint, stamps->normWindow2, stamps->normWindow1, mode);
     909        status = calculateMatrixVector(stamp->matrix, stamp->vector, stamps->normValue, stamp->image1, stamp->image2,
     910                                       weight, window, stamp->convolutions2, kernels, polyValues, footprint);
    800911        break;
    801912      case PM_SUBTRACTION_MODE_DUAL:
    802         status = calculateDualMatrixVector(stamp->matrix, stamp->vector, &stamp->norm,
    803                                            stamp->image1, stamp->image2,
    804                                            weight, window, stamp->convolutions1, stamp->convolutions2,
    805                                            kernels, polyValues, footprint, stamps->normWindow1, stamps->normWindow2, mode);
     913        status = calculateDualMatrixVector(stamp, stamps->normValue, stamps->normValue2, weight, window, kernels, polyValues, footprint);
    806914        break;
    807915      default:
     
    9281036    int numKernels = kernels->num;      // Number of kernel basis functions
    9291037    int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations
    930     int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    931     int numParams = numKernels * numSpatial + 1 + numBackground;    // Number of parameters being solved for
    932     int numSolution1 = numParams, numSolution2 = 0;                 // Number of parameters for each solution
     1038    // XXX int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     1039    // XXX int numParams = numKernels * numSpatial + 1 + numBackground;    // Number of parameters being solved for
     1040    int numParams = numKernels * numSpatial;        // Number of parameters being solved for
     1041    int numSolution1 = numParams, numSolution2 = 0; // Number of parameters for each solution
    9331042    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    9341043        // An additional image is convolved
     
    9601069
    9611070    if (kernels->mode != PM_SUBTRACTION_MODE_DUAL) {
    962         // Accumulate the least-squares matricies and vectors
     1071        // Accumulate the least-squares matrices and vectors.  These are generated for the
     1072        // kernel elements, excluding the background and normalization.
    9631073        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64); // Combined matrix
    9641074        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64); // Combined vector
     
    9661076        psImageInit(sumMatrix, 0.0);
    9671077
    968         psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
    969 
    9701078        int numStamps = 0;              // Number of good stamps
     1079        for (int i = 0; i < stamps->num; i++) {
     1080            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     1081            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
     1082               
     1083                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     1084                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     1085
     1086                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
     1087                numStamps++;
     1088            } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) {
     1089                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
     1090            }
     1091        }
     1092
     1093#if 0
     1094        psImage *save = psImageCopy(NULL, sumMatrix, PS_TYPE_F32);
     1095        psFitsWriteImageSimple ("sumMatrix.fits", save, NULL);
     1096        psVectorWriteFile("sumVector.dat", sumVector);
     1097        psFree (save);
     1098#endif
     1099
     1100        psVector *solution = NULL;                       // Solution to equation!
     1101        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     1102        psVectorInit(solution, 0);
     1103
     1104        // XXX TEST: try some constraint on the svd solution
     1105        // solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1106        // SINGLE solution
     1107        if (1) {
     1108            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1109        } else {
     1110            psVector *PERM = NULL;
     1111            psImage *LU = psMatrixLUDecomposition(NULL, &PERM, sumMatrix);
     1112            solution = psMatrixLUSolution(solution, LU, sumVector, PERM);
     1113            psFree (LU);
     1114            psFree (PERM);
     1115        }
     1116
     1117# if (0)
     1118        for (int i = 0; i < solution->n; i++) {
     1119            psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "Single solution %d: %lf\n", i, solution->data.F64[i]);
     1120        }
     1121# endif
     1122
     1123        if (!kernels->solution1) {
     1124            kernels->solution1 = psVectorAlloc(sumVector->n + 2, PS_TYPE_F64); // 1 for norm, 1 for bg
     1125            psVectorInit(kernels->solution1, 0.0);
     1126        }
     1127
     1128        int numKernels = kernels->num;
     1129        int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     1130        int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     1131        for (int i = 0; i < numKernels * numPoly; i++) {
     1132            kernels->solution1->data.F64[i] = solution->data.F64[i];
     1133        }
     1134
     1135        // Apply the normalisation and background separately
     1136        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1137        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1138        kernels->solution1->data.F64[normIndex] = stamps->normValue;
     1139        kernels->solution1->data.F64[bgIndex] = 0.0;
     1140
     1141        psFree(solution);
     1142        psFree(sumVector);
     1143        psFree(sumMatrix);
     1144
     1145    } else {
     1146        // Dual convolution solution
     1147        // Accumulate the least-squares matrices and vectors.  These are generated for the
     1148        // kernel elements, excluding the background and normalization.
     1149        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     1150        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64);
     1151        psImageInit(sumMatrix, 0.0);
     1152        psVectorInit(sumVector, 0.0);
     1153
     1154        int numStamps = 0;         // Number of good stamps
     1155        double normSquare1 = 0.0; // Sum of (I_1)^2 over stamps
     1156        double normSquare2 = 0.0; // Sum of (I_2)^2 over stamps
    9711157        for (int i = 0; i < stamps->num; i++) {
    9721158            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    9741160                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
    9751161                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
    976                 psVectorAppend(norms, stamp->norm);
     1162
     1163                normSquare1 += stamp->normSquare1;
     1164                normSquare2 += stamp->normSquare2;
     1165
    9771166                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    9781167                numStamps++;
     
    9831172
    9841173#if 0
    985         int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    986         calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
     1174        psImage *save = psImageCopy(NULL, sumMatrix, PS_TYPE_F32);
     1175        psFitsWriteImageSimple ("sumMatrix.fits", save, NULL);
     1176        psVectorWriteFile("sumVector.dat", sumVector);
     1177        psFree (save);
    9871178#endif
     1179
     1180        if (PENALTY) {
     1181            calculatePenalty(sumMatrix, sumVector, kernels, normSquare1, normSquare2);
     1182        }
    9881183
    9891184        psVector *solution = NULL;                       // Solution to equation!
     
    9911186        psVectorInit(solution, 0);
    9921187
    993 #if 0
    994         // Regular, straight-forward solution
    995         solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    996 #else
    997         {
    998             // Solve normalisation and background separately
    999             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1000             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    1001 
    1002             psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
    1003             if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
    1004                 psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
    1005                 psFree(stats);
    1006                 psFree(sumMatrix);
    1007                 psFree(sumVector);
    1008                 psFree(norms);
    1009                 return false;
    1010             }
    1011 
    1012             // double normValue = 1.0;
    1013             double normValue = stats->robustMedian;
    1014             // double bgValue = 0.0;
    1015 
    1016             psFree(stats);
    1017 
    1018 #ifdef TESTING
    1019             fprintf(stderr, "Norm: %lf\n", normValue);
    1020 #endif
    1021             // Solve kernel components
    1022             for (int i = 0; i < numSolution1; i++) {
    1023                 sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
    1024 
    1025                 sumMatrix->data.F64[i][normIndex] = 0.0;
    1026                 sumMatrix->data.F64[normIndex][i] = 0.0;
    1027             }
    1028             sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
    1029             sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
    1030             sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
    1031 
    1032             sumMatrix->data.F64[normIndex][normIndex] = 1.0;
    1033             sumVector->data.F64[normIndex] = 0.0;
    1034 
    1035             solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    1036 
    1037             solution->data.F64[normIndex] = normValue;
    1038         }
    1039 # endif
    1040 
    1041 #if (1)
    1042         for (int i = 0; i < solution->n; i++) {
    1043             fprintf(stderr, "Single solution %d: %lf\n", i, solution->data.F64[i]);
    1044         }
    1045 #endif
    1046 
    1047         if (!kernels->solution1) {
    1048             kernels->solution1 = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    1049             psVectorInit(kernels->solution1, 0.0);
    1050         }
    1051 
    1052         // only update the solutions that we chose to calculate:
    1053         if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    1054             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1055             kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
    1056         }
    1057         if (mode & PM_SUBTRACTION_EQUATION_BG) {
    1058             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    1059             kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
    1060         }
    1061         if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    1062             int numKernels = kernels->num;
    1063             int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
    1064             int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    1065             for (int i = 0; i < numKernels * numPoly; i++) {
    1066                 kernels->solution1->data.F64[i] = solution->data.F64[i];
    1067             }
    1068         }
    1069 
    1070         psFree(norms);
    1071         psFree(solution);
    1072         psFree(sumVector);
    1073         psFree(sumMatrix);
    1074 
    1075 #ifdef TESTING
    1076         // XXX double-check for NAN in data:
    1077         for (int ix = 0; ix < kernels->solution1->n; ix++) {
    1078             if (!isfinite(kernels->solution1->data.F64[ix])) {
    1079                 fprintf (stderr, "WARNING: NAN in vector\n");
    1080             }
    1081         }
    1082 #endif
    1083 
    1084     } else {
    1085         // Dual convolution solution
    1086 
    1087         // Accumulation of stamp matrices/vectors
    1088         psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    1089         psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64);
    1090         psImageInit(sumMatrix, 0.0);
    1091         psVectorInit(sumVector, 0.0);
    1092 
    1093         psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
    1094 
    1095         int numStamps = 0;              // Number of good stamps
    1096         for (int i = 0; i < stamps->num; i++) {
    1097             pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    1098             if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    1099                 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
    1100                 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
    1101 
    1102                 psVectorAppend(norms, stamp->norm);
    1103 
    1104                 pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    1105                 numStamps++;
    1106             }
    1107         }
    1108 
    1109 #ifdef TESTING
    1110         psFitsWriteImageSimple ("sumMatrix.fits", sumMatrix, NULL);
    1111         psVectorWriteFile("sumVector.dat", sumVector);
    1112 #endif
    1113 
    1114 #if 1
    1115         // int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    1116         // calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
    1117 
    1118         int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1119         calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 100.0);
    1120 #endif
    1121 
    1122         psVector *solution = NULL;                       // Solution to equation!
    1123         solution = psVectorAlloc(numParams, PS_TYPE_F64);
    1124         psVectorInit(solution, 0);
    1125 
    1126 #if 0
    1127         // Regular, straight-forward solution
    1128         solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    1129 #else
    1130         {
    1131             // Solve normalisation and background separately
    1132             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1133             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    1134 
    1135 #if 0
    1136             psImage *normMatrix = psImageAlloc(2, 2, PS_TYPE_F64);
    1137             psVector *normVector = psVectorAlloc(2, PS_TYPE_F64);
    1138 
    1139             normMatrix->data.F64[0][0] = sumMatrix->data.F64[normIndex][normIndex];
    1140             normMatrix->data.F64[1][1] = sumMatrix->data.F64[bgIndex][bgIndex];
    1141             normMatrix->data.F64[0][1] = normMatrix->data.F64[1][0] = sumMatrix->data.F64[normIndex][bgIndex];
    1142 
    1143             normVector->data.F64[0] = sumVector->data.F64[normIndex];
    1144             normVector->data.F64[1] = sumVector->data.F64[bgIndex];
    1145 
    1146             psVector *normSolution = psMatrixSolveSVD(NULL, normMatrix, normVector, NAN);
    1147 
    1148             double normValue = normSolution->data.F64[0];
    1149             double bgValue = normSolution->data.F64[1];
    1150 
    1151             psFree(normMatrix);
    1152             psFree(normVector);
    1153             psFree(normSolution);
    1154 #endif
    1155 
    1156             psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
    1157             if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
    1158                 psError(PM_ERR_DATA, false, "Unable to determine median normalisation");
    1159                 psFree(stats);
    1160                 psFree(sumMatrix);
    1161                 psFree(sumVector);
    1162                 psFree(norms);
    1163                 return false;
    1164             }
    1165 
    1166             double normValue = stats->robustMedian;
    1167 
    1168             psFree(stats);
    1169 
    1170 #ifdef TESTING
    1171             fprintf(stderr, "Norm: %lf\n", normValue);
    1172 #endif
    1173 
    1174             // Solve kernel components
    1175             for (int i = 0; i < numSolution2; i++) {
    1176                 sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
    1177                 sumVector->data.F64[i + numSolution1] -= normValue * sumMatrix->data.F64[normIndex][i + numSolution1];
    1178 
    1179                 sumMatrix->data.F64[i][normIndex] = 0.0;
    1180                 sumMatrix->data.F64[normIndex][i] = 0.0;
    1181 
    1182                 sumMatrix->data.F64[i + numSolution1][normIndex] = 0.0;
    1183                 sumMatrix->data.F64[normIndex][i + numSolution1] = 0.0;
    1184             }
    1185             sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
    1186             sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
    1187             sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
    1188 
    1189             sumMatrix->data.F64[normIndex][normIndex] = 1.0;
    1190 
    1191             sumVector->data.F64[normIndex] = 0.0;
    1192 
    1193             solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
    1194 
    1195             solution->data.F64[normIndex] = normValue;
    1196         }
    1197 #endif
    1198 
    1199 
    1200 #if (1)
     1188        // DUAL solution
     1189        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1190
     1191#if (0)
    12011192        for (int i = 0; i < solution->n; i++) {
    12021193            fprintf(stderr, "Dual solution %d: %lf\n", i, solution->data.F64[i]);
     
    12041195#endif
    12051196
    1206         psFree(sumMatrix);
    1207         psFree(sumVector);
    1208 
    1209         psFree(norms);
    1210 
    12111197        if (!kernels->solution1) {
    1212             kernels->solution1 = psVectorAlloc(numSolution1, PS_TYPE_F64);
     1198            kernels->solution1 = psVectorAlloc(numSolution1 + 2, PS_TYPE_F64);
    12131199            psVectorInit (kernels->solution1, 0.0);
    12141200        }
     
    12181204        }
    12191205
    1220         // only update the solutions that we chose to calculate:
    1221         if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    1222             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1223             kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
    1224         }
    1225         if (mode & PM_SUBTRACTION_EQUATION_BG) {
    1226             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    1227             kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
    1228         }
    1229         if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    1230             int numKernels = kernels->num;
    1231             for (int i = 0; i < numKernels * numSpatial; i++) {
    1232                 // XXX fprintf (stderr, "keep\n");
    1233                 kernels->solution1->data.F64[i] = solution->data.F64[i];
    1234                 kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
    1235             }
    1236         }
    1237 
    1238 
    1239         memcpy(kernels->solution1->data.F64, solution->data.F64,
    1240                numSolution1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    1241         memcpy(kernels->solution2->data.F64, &solution->data.F64[numSolution1],
    1242                numSolution2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    1243 
     1206        // for DUAL convolution analysis, we apply the normalization to I1 as follows:
     1207        // I1c = norm I1 + \Sum_i a_i norm I1 \cross k_i
     1208        // I2c =      I2 + \Sum_i b_i      I2 \cross k_i
     1209
     1210        // We absorb the normalization into a_i after the analysis is complete to be consistent
     1211        // with the SINGLE definitions of the convolutions
     1212
     1213        int numKernels = kernels->num;
     1214        for (int i = 0; i < numKernels * numSpatial; i++) {
     1215            // we solve for coefficients
     1216            kernels->solution1->data.F64[i] = solution->data.F64[i] * stamps->normValue;
     1217            kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
     1218        }
     1219
     1220        // Apply the normalisation and background separately
     1221        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1222        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1223        kernels->solution1->data.F64[normIndex] = stamps->normValue;
     1224        kernels->solution1->data.F64[bgIndex] = 0.0;
     1225
     1226        psFree(sumMatrix);
     1227        psFree(sumVector);
    12441228        psFree(solution);
    1245 
    12461229    }
    12471230
     
    12651248}
    12661249
    1267 bool pmSubtractionResidualStats(psVector *fSigRes, psVector *fMaxRes, psVector *fMinRes, psKernel *target, psKernel *source, psKernel *residual, double norm, int footprint) {
    1268 
    1269     // XXX measure some useful stats on the residuals
     1250// measure some useful stats on the stamp residuals:
     1251// fResSigma : the residual stdev / total flux
     1252// fResOuter : the residual fabs / total flux for R > 2 pix
     1253// fResTotal : the residual fabs / total flux for R > 0 pix
     1254bool pmSubtractionResidualStats(psVector *fResSigma, psVector *fResOuter, psVector *fResTotal, psKernel *target, psKernel *source, psKernel *residual, double norm, int footprint) {
     1255
    12701256    float sum = 0.0;
    12711257    float peak = 0.0;
     
    12771263    }
    12781264
    1279     // only count pixels with more than X% of the source flux
    1280     // calculate stdev(dflux)
     1265    // init counters
     1266    int npix = 0;
    12811267    float dflux1 = 0.0;
    12821268    float dflux2 = 0.0;
    1283     int npix = 0;
    1284 
    1285     float dmax = 0.0;
    1286     float dmin = 0.0;
     1269    float dOuter = 0.0;
     1270    float dTotal = 0.0;
    12871271
    12881272    for (int y = - footprint; y <= footprint; y++) {
    12891273        for (int x = - footprint; x <= footprint; x++) {
    1290             float dflux = 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
    1291             if (dflux < 0.02*sum) continue;
    12921274            dflux1 += residual->kernel[y][x];
    12931275            dflux2 += PS_SQR(residual->kernel[y][x]);
    1294             dmax = PS_MAX(residual->kernel[y][x], dmax);
    1295             dmin = PS_MIN(residual->kernel[y][x], dmin);
     1276            dTotal += fabs(residual->kernel[y][x]);
     1277            if (hypot(x,y) > 2.0) {
     1278              dOuter += fabs(residual->kernel[y][x]);
     1279            }
    12961280            npix ++;
    12971281        }
     
    12991283    float sigma = sqrt(dflux2 / npix - PS_SQR(dflux1/npix));
    13001284    if (!isfinite(sum))  return false;
    1301     if (!isfinite(dmax)) return false;
    1302     if (!isfinite(dmin)) return false;
    13031285    if (!isfinite(peak)) return false;
    1304 
    1305     // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dmax/peak, dmin/peak);
    1306     psVectorAppend(fSigRes, sigma/sum);
    1307     psVectorAppend(fMaxRes, dmax/peak);
    1308     psVectorAppend(fMinRes, dmin/peak);
     1286    if (!isfinite(dOuter)) return false;
     1287    if (!isfinite(dTotal)) return false;
     1288
     1289    // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dOuter/sum, dTotal/sum);
     1290    psVectorAppend(fResSigma, sigma/sum);
     1291    psVectorAppend(fResOuter, dOuter/sum);
     1292    psVectorAppend(fResTotal, dTotal/sum);
    13091293    return true;
    13101294}
     
    13291313    pmSubtractionVisualShowFitInit (stamps);
    13301314
    1331     psVector *fSigRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
    1332     psVector *fMinRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
    1333     psVector *fMaxRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1315    psVector *fResSigma = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1316    psVector *fResOuter = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1317    psVector *fResTotal = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
    13341318
    13351319    // we want to save the residual images for the 9 brightest stamps.
     
    14431427            }
    14441428
    1445             pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, target, source, residual, norm, footprint);
     1429            pmSubtractionResidualStats(fResSigma, fResOuter, fResTotal, target, source, residual, norm, footprint);
    14461430
    14471431        } else {
     
    14791463            }
    14801464
    1481             pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, image1, image2, residual, norm, footprint);
     1465            pmSubtractionResidualStats(fResSigma, fResOuter, fResTotal, image1, image2, residual, norm, footprint);
    14821466        }
    14831467
     
    15581542
    15591543        psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
    1560         psVectorStats (stats, fSigRes, NULL, NULL, 0);
    1561         kernels->fSigResMean = stats->robustMedian;
    1562         kernels->fSigResStdev = stats->robustStdev;
     1544        psVectorStats (stats, fResSigma, NULL, NULL, 0);
     1545        kernels->fResSigmaMean = stats->robustMedian;
     1546        kernels->fResSigmaStdev = stats->robustStdev;
    15631547
    15641548        psStatsInit (stats);
    1565         psVectorStats (stats, fMaxRes, NULL, NULL, 0);
    1566         kernels->fMaxResMean = stats->robustMedian;
    1567         kernels->fMaxResStdev = stats->robustStdev;
     1549        psVectorStats (stats, fResOuter, NULL, NULL, 0);
     1550        kernels->fResOuterMean = stats->robustMedian;
     1551        kernels->fResOuterStdev = stats->robustStdev;
    15681552
    15691553        psStatsInit (stats);
    1570         psVectorStats (stats, fMinRes, NULL, NULL, 0);
    1571         kernels->fMinResMean = stats->robustMedian;
    1572         kernels->fMinResStdev = stats->robustStdev;
     1554        psVectorStats (stats, fResTotal, NULL, NULL, 0);
     1555        kernels->fResTotalMean = stats->robustMedian;
     1556        kernels->fResTotalStdev = stats->robustStdev;
    15731557
    15741558        // XXX save these values somewhere
    1575         psLogMsg("psModules.imcombine", PS_LOG_INFO, "fSigma: %f +/- %f, fMaxRes: %f +/- %f, fMinRes: %f +/- %f",
    1576                  kernels->fSigResMean, kernels->fSigResStdev,
    1577                  kernels->fMaxResMean, kernels->fMaxResStdev,
    1578                  kernels->fMinResMean, kernels->fMinResStdev);
    1579 
    1580         psFree (fSigRes);
    1581         psFree (fMaxRes);
    1582         psFree (fMinRes);
     1559        psLogMsg("psModules.imcombine", PS_LOG_INFO, "fResSigma: %f +/- %f, fResOuter: %f +/- %f, fResTotal: %f +/- %f",
     1560                 kernels->fResSigmaMean, kernels->fResSigmaStdev,
     1561                 kernels->fResOuterMean, kernels->fResOuterStdev,
     1562                 kernels->fResTotalMean, kernels->fResTotalStdev);
     1563
     1564        psFree (fResSigma);
     1565        psFree (fResOuter);
     1566        psFree (fResTotal);
    15831567        psFree (stats);
    15841568    }
     
    15861570    psFree(residual);
    15871571    psFree(polyValues);
    1588 
    15891572
    15901573    return deviations;
     
    19121895    return true;
    19131896}
    1914 
    1915 
    1916 # if 0
    1917 
    1918 #ifdef TESTING
    1919         psFitsWriteImageSimple("A.fits", sumMatrix, NULL);
    1920         psVectorWriteFile ("B.dat", sumVector);
    1921 #endif
    1922 
    1923 # define SVD_ANALYSIS 0
    1924 # define COEFF_SIG 0.0
    1925 # define SVD_TOL 0.0
    1926 
    1927         // Use SVD to determine the kernel coeffs (and validate)
    1928         if (SVD_ANALYSIS) {
    1929 
    1930             // We have sumVector and sumMatrix.  we are trying to solve the following equation:
    1931             // sumMatrix * x = sumVector.
    1932 
    1933             // we can use any standard matrix inversion to solve this.  However, the basis
    1934             // functions in general have substantial correlation, so that the solution may be
    1935             // somewhat poorly determined or unstable.  If not numerically ill-conditioned, the
    1936             // system of equations may be statistically ill-conditioned.  Noise in the image
    1937             // will drive insignificant, but correlated, terms in the solution.  To avoid these
    1938             // problems, we can use SVD to identify numerically unconstrained values and to
    1939             // avoid statistically badly determined value.
    1940 
    1941             // A = sumMatrix, B = sumVector
    1942             // SVD: A = U w V^T  -> A^{-1} = V (1/w) U^T
    1943             // x = V (1/w) (U^T B)
    1944             // \sigma_x = sqrt(diag(A^{-1}))
    1945             // solve for x and A^{-1} to get x & dx
    1946             // identify the elements of (1/w) that are nan (1/0.0) -> set to 0.0
    1947             // identify the elements of x that are insignificant (x / dx < 1.0? < 0.5?) -> set to 0.0
    1948 
    1949             // If I use the SVD trick to re-condition the matrix, I need to break out the
    1950             // kernel and normalization terms from the background term.
    1951             // XXX is this true?  or was this due to an error in the analysis?
    1952 
    1953             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    1954 
    1955             // now pull out the kernel elements into their own square matrix
    1956             psImage  *kernelMatrix = psImageAlloc  (sumMatrix->numCols - 1, sumMatrix->numRows - 1, PS_TYPE_F64);
    1957             psVector *kernelVector = psVectorAlloc (sumMatrix->numCols - 1, PS_TYPE_F64);
    1958 
    1959             for (int ix = 0, kx = 0; ix < sumMatrix->numCols; ix++) {
    1960                 if (ix == bgIndex) continue;
    1961                 for (int iy = 0, ky = 0; iy < sumMatrix->numRows; iy++) {
    1962                     if (iy == bgIndex) continue;
    1963                     kernelMatrix->data.F64[ky][kx] = sumMatrix->data.F64[iy][ix];
    1964                     ky++;
    1965                 }
    1966                 kernelVector->data.F64[kx] = sumVector->data.F64[ix];
    1967                 kx++;
    1968             }
    1969 
    1970             psImage *U = NULL;
    1971             psImage *V = NULL;
    1972             psVector *w = NULL;
    1973             if (!psMatrixSVD (&U, &w, &V, kernelMatrix)) {
    1974                 psError(psErrorCodeLast(), false, "failed to perform SVD on sumMatrix\n");
    1975                 return NULL;
    1976             }
    1977 
    1978             // calculate A_inverse:
    1979             // Ainv = V * w * U^T
    1980             psImage *wUt  = p_pmSubSolve_wUt (w, U);
    1981             psImage *Ainv = p_pmSubSolve_VwUt (V, wUt);
    1982             psImage *Xvar = NULL;
    1983             psFree (wUt);
    1984 
    1985 # ifdef TESTING
    1986             // kernel terms:
    1987             for (int i = 0; i < w->n; i++) {
    1988                 fprintf (stderr, "w: %f\n", w->data.F64[i]);
    1989             }
    1990 # endif
    1991             // loop over w adding in more and more of the values until chisquare is no longer
    1992             // dropping significantly.
    1993             // XXX this does not seem to work very well: we seem to need all terms even for
    1994             // simple cases...
    1995 
    1996             psVector *Xsvd = NULL;
    1997             {
    1998                 psVector *Ax = NULL;
    1999                 psVector *UtB = NULL;
    2000                 psVector *wUtB = NULL;
    2001 
    2002                 psVector *wApply = psVectorAlloc(w->n, PS_TYPE_F64);
    2003                 psVector *wMask = psVectorAlloc(w->n, PS_TYPE_U8);
    2004                 psVectorInit (wMask, 1); // start by masking everything
    2005 
    2006                 double chiSquareLast = NAN;
    2007                 int maxWeight = 0;
    2008 
    2009                 double Axx, Bx, y2;
    2010 
    2011                 // XXX this is an attempt to exclude insignificant modes.
    2012                 // it was not successful with the ISIS kernel set: removing even
    2013                 // the least significant mode leaves additional ringing / noise
    2014                 // because the terms are so coupled.
    2015                 for (int k = 0; false && (k < w->n); k++) {
    2016 
    2017                     // unmask the k-th weight
    2018                     wMask->data.U8[k] = 0;
    2019                     p_pmSubSolve_SetWeights(wApply, w, wMask);
    2020 
    2021                     // solve for x:
    2022                     // x = V * w * (U^T * B)
    2023                     p_pmSubSolve_UtB (&UtB, U, kernelVector);
    2024                     p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
    2025                     p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
    2026 
    2027                     // chi-square for this system of equations:
    2028                     // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
    2029                     // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
    2030                     p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
    2031                     p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
    2032                     p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
    2033                     p_pmSubSolve_y2 (&y2, kernels, stamps);
    2034 
    2035                     // apparently, this works (compare with the brute force value below
    2036                     double chiSquare = Axx - 2.0*Bx + y2;
    2037                     double deltaChi = (k == 0) ? chiSquare : chiSquareLast - chiSquare;
    2038                     chiSquareLast = chiSquare;
    2039 
    2040                     // fprintf (stderr, "chi square = %f, delta: %f\n", chiSquare, deltaChi);
    2041                     if (k && !maxWeight && (deltaChi < 1.0)) {
    2042                         maxWeight = k;
    2043                     }
    2044                 }
    2045 
    2046                 // keep all terms or we get extra ringing
    2047                 maxWeight = w->n;
    2048                 psVectorInit (wMask, 1);
    2049                 for (int k = 0; k < maxWeight; k++) {
    2050                     wMask->data.U8[k] = 0;
    2051                 }
    2052                 p_pmSubSolve_SetWeights(wApply, w, wMask);
    2053 
    2054                 // solve for x:
    2055                 // x = V * w * (U^T * B)
    2056                 p_pmSubSolve_UtB (&UtB, U, kernelVector);
    2057                 p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
    2058                 p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
    2059 
    2060                 // chi-square for this system of equations:
    2061                 // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
    2062                 // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
    2063                 p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
    2064                 p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
    2065                 p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
    2066                 p_pmSubSolve_y2 (&y2, kernels, stamps);
    2067 
    2068                 // apparently, this works (compare with the brute force value below
    2069                 double chiSquare = Axx - 2.0*Bx + y2;
    2070                 psLogMsg ("psModules.imcombine", PS_LOG_INFO, "model kernel with %d terms; chi square = %f\n", maxWeight, chiSquare);
    2071 
    2072                 // re-calculate A^{-1} to get new variances:
    2073                 // Ainv = V * w * U^T
    2074                 // XXX since we keep all terms, this is identical to Ainv
    2075                 psImage *wUt  = p_pmSubSolve_wUt (wApply, U);
    2076                 Xvar = p_pmSubSolve_VwUt (V, wUt);
    2077                 psFree (wUt);
    2078 
    2079                 psFree (Ax);
    2080                 psFree (UtB);
    2081                 psFree (wUtB);
    2082                 psFree (wApply);
    2083                 psFree (wMask);
    2084             }
    2085 
    2086             // copy the kernel solutions to the full solution vector:
    2087             solution = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    2088             solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    2089 
    2090             for (int ix = 0, kx = 0; ix < sumVector->n; ix++) {
    2091                 if (ix == bgIndex) {
    2092                     solution->data.F64[ix] = 0;
    2093                     solutionErr->data.F64[ix] = 0.001;
    2094                     continue;
    2095                 }
    2096                 solutionErr->data.F64[ix] = sqrt(Ainv->data.F64[kx][kx]);
    2097                 solution->data.F64[ix] = Xsvd->data.F64[kx];
    2098                 kx++;
    2099             }
    2100 
    2101             psFree (kernelMatrix);
    2102             psFree (kernelVector);
    2103 
    2104             psFree (U);
    2105             psFree (V);
    2106             psFree (w);
    2107 
    2108             psFree (Ainv);
    2109             psFree (Xsvd);
    2110         } else {
    2111             psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
    2112             psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
    2113             if (!luMatrix) {
    2114                 psError(PM_ERR_DATA, true, "LU Decomposition of least-squares matrix failed.\n");
    2115                 psFree(solution);
    2116                 psFree(sumVector);
    2117                 psFree(sumMatrix);
    2118                 psFree(luMatrix);
    2119                 psFree(permutation);
    2120                 return NULL;
    2121             }
    2122 
    2123             solution = psMatrixLUSolution(NULL, luMatrix, sumVector, permutation);
    2124             psFree(luMatrix);
    2125             psFree(permutation);
    2126             if (!solution) {
    2127                 psError(PM_ERR_DATA, true, "Failed to solve the least-squares system.\n");
    2128                 psFree(solution);
    2129                 psFree(sumVector);
    2130                 psFree(sumMatrix);
    2131                 return NULL;
    2132             }
    2133 
    2134             // XXX LUD does not provide A^{-1}?  fake the error for now
    2135             solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
    2136             for (int ix = 0; ix < sumVector->n; ix++) {
    2137                 solutionErr->data.F64[ix] = 0.1*solution->data.F64[ix];
    2138             }
    2139         }
    2140 
    2141         if (!kernels->solution1) {
    2142             kernels->solution1 = psVectorAlloc (sumVector->n, PS_TYPE_F64);
    2143             psVectorInit (kernels->solution1, 0.0);
    2144         }
    2145 
    2146         // only update the solutions that we chose to calculate:
    2147         if (mode & PM_SUBTRACTION_EQUATION_NORM) {
    2148             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    2149             kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
    2150         }
    2151         if (mode & PM_SUBTRACTION_EQUATION_BG) {
    2152             int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
    2153             kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
    2154         }
    2155         if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
    2156             int numKernels = kernels->num;
    2157             int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
    2158             int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
    2159             for (int i = 0; i < numKernels * numPoly; i++) {
    2160                 // XXX fprintf (stderr, "%f +/- %f (%f) -> ", solution->data.F64[i], solutionErr->data.F64[i], fabs(solution->data.F64[i]/solutionErr->data.F64[i]));
    2161                 if (fabs(solution->data.F64[i] / solutionErr->data.F64[i]) < COEFF_SIG) {
    2162                     // XXX fprintf (stderr, "drop\n");
    2163                     kernels->solution1->data.F64[i] = 0.0;
    2164                 } else {
    2165                     // XXX fprintf (stderr, "keep\n");
    2166                     kernels->solution1->data.F64[i] = solution->data.F64[i];
    2167                 }
    2168             }
    2169         }
    2170         // double chiSquare = p_pmSubSolve_ChiSquare (kernels, stamps);
    2171         // fprintf (stderr, "chi square Brute = %f\n", chiSquare);
    2172 
    2173         psFree(solution);
    2174         psFree(sumVector);
    2175         psFree(sumMatrix);
    2176 # endif
    2177 
    2178 #ifdef TESTING
    2179               // XXX double-check for NAN in data:
    2180                 for (int iy = 0; iy < stamp->matrix->numRows; iy++) {
    2181                     for (int ix = 0; ix < stamp->matrix->numCols; ix++) {
    2182                         if (!isfinite(stamp->matrix->data.F64[iy][ix])) {
    2183                             fprintf (stderr, "WARNING: NAN in matrix\n");
    2184                         }
    2185                     }
    2186                 }
    2187                 for (int ix = 0; ix < stamp->vector->n; ix++) {
    2188                     if (!isfinite(stamp->vector->data.F64[ix])) {
    2189                         fprintf (stderr, "WARNING: NAN in vector\n");
    2190                     }
    2191                 }
    2192 #endif
    2193 
    2194 #ifdef TESTING
    2195         for (int ix = 0; ix < sumVector->n; ix++) {
    2196             if (!isfinite(sumVector->data.F64[ix])) {
    2197                 fprintf (stderr, "WARNING: NAN in vector\n");
    2198             }
    2199         }
    2200 #endif
    2201 
    2202 #ifdef TESTING
    2203         for (int ix = 0; ix < sumVector->n; ix++) {
    2204             if (!isfinite(sumVector->data.F64[ix])) {
    2205                 fprintf (stderr, "WARNING: NAN in vector\n");
    2206             }
    2207         }
    2208         {
    2209             psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
    2210             psFitsWriteImageSimple("matrixInv.fits", inverse, NULL);
    2211             psFree(inverse);
    2212         }
    2213         {
    2214             psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
    2215             psImage *Xt = psMatrixTranspose(NULL, X);
    2216             psImage *XtX = psMatrixMultiply(NULL, Xt, X);
    2217             psFitsWriteImageSimple("matrixErr.fits", XtX, NULL);
    2218             psFree(X);
    2219             psFree(Xt);
    2220             psFree(XtX);
    2221         }
    2222 #endif
    2223 
Note: See TracChangeset for help on using the changeset viewer.