IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Feb 10, 2010, 7:34:39 PM (16 years ago)
Author:
eugene
Message:

updates from eam_branches/20091201 (substantially changes to the kernel matching code; updates to pmDetection container, added pmPattern, etc)

File:
1 edited

Legend:

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

    r26035 r26893  
    1717//#define TESTING                         // TESTING output for debugging; may not work with threads!
    1818
    19 #define USE_WEIGHT                      // Include weight (1/variance) in equation?
     19//#define USE_WEIGHT                      // Include weight (1/variance) in equation?
     20//#define USE_WINDOW                      // Include weight (1/variance) in equation?
    2021
    2122
     
    2728static bool calculateMatrixVector(psImage *matrix, // Least-squares matrix, updated
    2829                                  psVector *vector, // Least-squares vector, updated
     30                                  double *norm,     // Normalisation, updated
    2931                                  const psKernel *input, // Input image (target)
    3032                                  const psKernel *reference, // Reference image (convolution source)
    3133                                  const psKernel *weight,  // Weight image
     34                                  const psKernel *window,  // Window image
    3235                                  const psArray *convolutions,         // Convolutions for each kernel
    3336                                  const pmSubtractionKernels *kernels, // Kernels
    3437                                  const psImage *polyValues, // Spatial polynomial values
    35                                   int footprint // (Half-)Size of stamp
     38                                  int footprint, // (Half-)Size of stamp
     39                                  int normWindow, // Window (half-)size for normalisation measurement
     40                                  const pmSubtractionEquationCalculationMode mode
    3641                                  )
    3742{
     
    5156
    5257    // Evaluate polynomial-polynomial terms
     58    // XXX we can skip this if we are not calculating kernel coeffs
    5359    for (int iyOrder = 0, iIndex = 0; iyOrder <= spatialOrder; iyOrder++) {
    5460        for (int ixOrder = 0; ixOrder <= spatialOrder - iyOrder; ixOrder++, iIndex++) {
     
    6470    }
    6571
     72    // initialize the matrix and vector for NOP on all coeffs.  we only fill in the coeffs we
     73    // choose to calculate
     74    psImageInit(matrix, 0.0);
     75    psVectorInit(vector, 1.0);
     76    for (int i = 0; i < matrix->numCols; i++) {
     77        matrix->data.F64[i][i] = 1.0;
     78    }
     79
     80    // the order of the elements in the matrix and vector is:
     81    // [kernel 0, x^0 y^0][kernel 1 x^0 y^0]...[kernel N, x^0 y^0]
     82    // [kernel 0, x^1 y^0][kernel 1 x^1 y^0]...[kernel N, x^1 y^0]
     83    // [kernel 0, x^n y^m][kernel 1 x^n y^m]...[kernel N, x^n y^m]
     84    // normalization
     85    // bg 0, bg 1, bg 2 (only 0 is currently used?)
    6686
    6787    for (int i = 0; i < numKernels; i++) {
     
    7494                for (int x = - footprint; x <= footprint; x++) {
    7595                    double cc = iConv->kernel[y][x] * jConv->kernel[y][x];
    76 #ifdef USE_WEIGHT
    77                     cc *= weight->kernel[y][x];
    78 #endif
     96                    if (weight) {
     97                        cc *= weight->kernel[y][x];
     98                    }
     99                    if (window) {
     100                        cc *= window->kernel[y][x];
     101                    }
    79102                    sumCC += cc;
    80103                }
    81104            }
    82105
    83             // Spatial variation
    84             for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    85                 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    86                     double value = sumCC * poly2[iTerm][jTerm];
    87                     matrix->data.F64[iIndex][jIndex] = value;
    88                     matrix->data.F64[jIndex][iIndex] = value;
     106            // Spatial variation of kernel coeffs
     107            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     108                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     109                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     110                        double value = sumCC * poly2[iTerm][jTerm];
     111                        matrix->data.F64[iIndex][jIndex] = value;
     112                        matrix->data.F64[jIndex][iIndex] = value;
     113                    }
    89114                }
    90115            }
     
    102127                double rc = ref * conv;
    103128                double c = conv;
    104 #ifdef USE_WEIGHT
    105                 float wtVal = weight->kernel[y][x];
    106                 ic *= wtVal;
    107                 rc *= wtVal;
    108                 c *= wtVal;
    109 #endif
     129                if (weight) {
     130                    float wtVal = weight->kernel[y][x];
     131                    ic *= wtVal;
     132                    rc *= wtVal;
     133                    c *= wtVal;
     134                }
     135                if (window) {
     136                    float winVal = window->kernel[y][x];
     137                    ic *= winVal;
     138                    rc *= winVal;
     139                    c  *= winVal;
     140                }
    110141                sumIC += ic;
    111142                sumRC += rc;
     
    117148            double normTerm = sumRC * poly[iTerm];
    118149            double bgTerm = sumC * poly[iTerm];
    119             matrix->data.F64[iIndex][normIndex] = normTerm;
    120             matrix->data.F64[normIndex][iIndex] = normTerm;
    121             matrix->data.F64[iIndex][bgIndex] = bgTerm;
    122             matrix->data.F64[bgIndex][iIndex] = bgTerm;
    123             vector->data.F64[iIndex] = sumIC * poly[iTerm];
     150            if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     151                matrix->data.F64[iIndex][normIndex] = normTerm;
     152                matrix->data.F64[normIndex][iIndex] = normTerm;
     153            }
     154            if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     155                matrix->data.F64[iIndex][bgIndex] = bgTerm;
     156                matrix->data.F64[bgIndex][iIndex] = bgTerm;
     157            }
     158            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     159                vector->data.F64[iIndex] = sumIC * poly[iTerm];
     160                if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
     161                    // subtract norm * sumRC * poly[iTerm]
     162                    psAssert (kernels->solution1, "programming error: define solution first!");
     163                    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     164                    double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
     165                    vector->data.F64[iIndex] -= norm * normTerm;
     166                }
     167            }
    124168        }
    125169    }
     
    130174    double sumR = 0.0;                  // Sum of the reference
    131175    double sumI = 0.0;                  // Sum of the input
     176    double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    132177    for (int y = - footprint; y <= footprint; y++) {
    133178        for (int x = - footprint; x <= footprint; x++) {
     
    137182            double rr = PS_SQR(ref);
    138183            double one = 1.0;
    139 #ifdef USE_WEIGHT
    140             float wtVal = weight->kernel[y][x];
    141             rr *= wtVal;
    142             ir *= wtVal;
    143             in *= wtVal;
    144             ref *= wtVal;
    145             one *= wtVal;
    146 #endif
     184
     185            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
     186                normI1 += ref;
     187                normI2 += in;
     188            }
     189
     190            if (weight) {
     191                float wtVal = weight->kernel[y][x];
     192                rr *= wtVal;
     193                ir *= wtVal;
     194                in *= wtVal;
     195                ref *= wtVal;
     196                one *= wtVal;
     197            }
     198            if (window) {
     199                float  winVal = window->kernel[y][x];
     200                rr      *= winVal;
     201                ir      *= winVal;
     202                in      *= winVal;
     203                ref *= winVal;
     204                one *= winVal;
     205            }
    147206            sumRR += rr;
    148207            sumIR += ir;
     
    152211        }
    153212    }
    154     matrix->data.F64[normIndex][normIndex] = sumRR;
    155     matrix->data.F64[bgIndex][bgIndex] = sum1;
    156     matrix->data.F64[normIndex][bgIndex] = matrix->data.F64[bgIndex][normIndex] = sumR;
    157     vector->data.F64[normIndex] = sumIR;
    158     vector->data.F64[bgIndex] = sumI;
     213
     214    *norm = normI2 / normI1;
     215
     216    if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     217        matrix->data.F64[normIndex][normIndex] = sumRR;
     218        vector->data.F64[normIndex] = sumIR;
     219        // subtract sum over kernels * kernel solution
     220    }
     221    if (mode & PM_SUBTRACTION_EQUATION_BG) {
     222        matrix->data.F64[bgIndex][bgIndex] = sum1;
     223        vector->data.F64[bgIndex] = sumI;
     224    }
     225    if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
     226        matrix->data.F64[normIndex][bgIndex] = sumR;
     227        matrix->data.F64[bgIndex][normIndex] = sumR;
     228    }
     229
     230    // check for any NAN values in the result, skip if found:
     231    for (int iy = 0; iy < matrix->numRows; iy++) {
     232        for (int ix = 0; ix < matrix->numCols; ix++) {
     233            if (!isfinite(matrix->data.F64[iy][ix])) {
     234                fprintf (stderr, "WARNING: NAN in matrix\n");
     235                return false;
     236            }
     237        }
     238    }
     239    for (int ix = 0; ix < vector->n; ix++) {
     240        if (!isfinite(vector->data.F64[ix])) {
     241            fprintf (stderr, "WARNING: NAN in vector\n");
     242            return false;
     243        }
     244    }
    159245
    160246    return true;
    161247}
    162248
     249
    163250// Calculate the least-squares matrix and vector for dual convolution
    164 static bool calculateDualMatrixVector(psImage *matrix1, // Least-squares matrix, updated
    165                                       psVector *vector1, // Least-squares vector, updated
    166                                       psImage *matrix2,  // Least-squares matrix, updated
    167                                       psVector *vector2, // Least-squares vector, updated
    168                                       psImage *matrixX,  // Cross-matrix
     251static bool calculateDualMatrixVector(psImage *matrix, // Least-squares matrix, updated
     252                                      psVector *vector, // Least-squares vector, updated
     253                                      double *norm,     // Normalisation, updated
    169254                                      const psKernel *image1, // Image 1
    170255                                      const psKernel *image2, // Image 2
    171256                                      const psKernel *weight,  // Weight image
     257                                      const psKernel *window,  // Window image
    172258                                      const psArray *convolutions1, // Convolutions of image 1 for each kernel
    173259                                      const psArray *convolutions2, // Convolutions of image 2 for each kernel
    174260                                      const pmSubtractionKernels *kernels, // Kernels
    175261                                      const psImage *polyValues, // Spatial polynomial values
    176                                       int footprint // (Half-)Size of stamp
     262                                      int footprint, // (Half-)Size of stamp
     263                                      int normWindow, // Window (half-)size for normalisation measurement
     264                                      const pmSubtractionEquationCalculationMode mode
    177265                                      )
    178266{
    179     // A_ij = A_i A_j
    180     // B_ij = B_i B_j
    181     // C_ij = A_i B_j
    182     // d_i = A_i I_2
    183     // e_i = B_i I_2
    184 
    185     // A_i = I_1 * k_i
    186     // B_i = I_2 * k_i
    187 
    188     // Background: A_i = 1.0
    189     // Normalisation: A_i = I_1
    190 
    191267    int numKernels = kernels->num;                      // Number of kernels
    192268    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     
    196272    double poly[numPoly];                                 // Polynomial terms
    197273    double poly2[numPoly][numPoly];                       // Polynomial-polynomial values
     274
     275    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     276    int numParams = numKernels * numPoly + 1 + numBackground;       // Number of regular parameters
     277    int numParams2 = numKernels * numPoly;                          // Number of additional parameters for dual
     278    int numDual = numParams + numParams2;                           // Total number of parameters for dual
     279
     280    psAssert(matrix &&
     281             matrix->type.type == PS_TYPE_F64 &&
     282             matrix->numCols == numDual &&
     283             matrix->numRows == numDual,
     284             "Least-squares matrix is bad.");
     285    psAssert(vector &&
     286             vector->type.type == PS_TYPE_F64 &&
     287             vector->n == numDual,
     288             "Least-squares vector is bad.");
    198289
    199290    // Evaluate polynomial-polynomial terms
     
    212303
    213304
     305    // initialize the matrix and vector for NOP on all coeffs.  we only fill in the coeffs we
     306    // choose to calculate
     307    psImageInit(matrix, 0.0);
     308    psVectorInit(vector, 1.0);
     309    for (int i = 0; i < matrix->numCols; i++) {
     310        matrix->data.F64[i][i] = 1.0;
     311    }
     312
    214313    for (int i = 0; i < numKernels; i++) {
    215314        psKernel *iConv1 = convolutions1->data[i]; // Convolution 1 for index i
     
    219318            psKernel *jConv2 = convolutions2->data[j]; // Convolution 2 for index j
    220319
    221             double sumAA = 0.0;         // Sum of convolution products for matrix A
    222             double sumBB = 0.0;         // Sum of convolution products for matrix B
    223             double sumAB = 0.0;         // Sum of convolution products for matrix C
     320            double sumAA = 0.0;         // Sum of convolution products between image 1
     321            double sumBB = 0.0;         // Sum of convolution products between image 2
     322            double sumAB = 0.0;         // Sum of convolution products across images 1 and 2
    224323            for (int y = - footprint; y <= footprint; y++) {
    225324                for (int x = - footprint; x <= footprint; x++) {
     
    227326                    double bb = iConv2->kernel[y][x] * jConv2->kernel[y][x];
    228327                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
    229 #ifdef USE_WEIGHT
    230                     float wtVal = weight->kernel[y][x];
    231                     aa *= wtVal;
    232                     bb *= wtVal;
    233                     ab *= wtVal;
    234 #endif
     328                    if (weight) {
     329                        float wtVal = weight->kernel[y][x];
     330                        aa *= wtVal;
     331                        bb *= wtVal;
     332                        ab *= wtVal;
     333                    }
     334                    if (window) {
     335                        float wtVal = window->kernel[y][x];
     336                        aa *= wtVal;
     337                        bb *= wtVal;
     338                        ab *= wtVal;
     339                    }
    235340                    sumAA += aa;
    236341                    sumBB += bb;
     
    239344            }
    240345
    241             // Spatial variation
    242             for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    243                 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    244                     double aa = sumAA * poly2[iTerm][jTerm];
    245                     double bb = sumBB * poly2[iTerm][jTerm];
    246                     double ab = sumAB * poly2[iTerm][jTerm];
    247                     matrix1->data.F64[iIndex][jIndex] = aa;
    248                     matrix1->data.F64[jIndex][iIndex] = aa;
    249                     matrix2->data.F64[iIndex][jIndex] = bb;
    250                     matrix2->data.F64[jIndex][iIndex] = bb;
    251                     matrixX->data.F64[iIndex][jIndex] = ab;
     346            // Spatial variation of kernel coeffs
     347            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     348                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     349                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     350                        double aa = sumAA * poly2[iTerm][jTerm];
     351                        double bb = sumBB * poly2[iTerm][jTerm];
     352                        double ab = sumAB * poly2[iTerm][jTerm];
     353
     354                        matrix->data.F64[iIndex][jIndex] = aa;
     355                        matrix->data.F64[jIndex][iIndex] = aa;
     356
     357                        matrix->data.F64[iIndex + numParams][jIndex + numParams] = bb;
     358                        matrix->data.F64[jIndex + numParams][iIndex + numParams] = bb;
     359
     360                        matrix->data.F64[iIndex][jIndex + numParams] = ab;
     361                        matrix->data.F64[jIndex + numParams][iIndex] = ab;
     362                    }
    252363                }
    253364            }
     
    259370                for (int x = - footprint; x <= footprint; x++) {
    260371                    double ab = iConv1->kernel[y][x] * jConv2->kernel[y][x];
    261 #ifdef USE_WEIGHT
    262                     ab *= weight->kernel[y][x];
    263 #endif
     372                    if (weight) {
     373                        ab *= weight->kernel[y][x];
     374                    }
     375                    if (window) {
     376                        ab *= window->kernel[y][x];
     377                    }
    264378                    sumAB += ab;
    265379                }
    266380            }
    267381
    268             // Spatial variation
    269             for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
    270                 for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
    271                     double ab = sumAB * poly2[iTerm][jTerm];
    272                     matrixX->data.F64[iIndex][jIndex] = ab;
    273                 }
    274             }
    275         }
    276 
    277         double sumAI2 = 0.0;            // Sum of A.I_2 products (for vector 1)
    278         double sumBI2 = 0.0;            // Sum of B.I_2 products (for vector 2)
    279         double sumAI1 = 0.0;            // Sum of A.I_1 products (for matrix 1, normalisation)
    280         double sumA = 0.0;              // Sum of A (for matrix 1, background)
    281         double sumBI1 = 0.0;            // Sum of B.I_1 products (for matrix X, normalisation)
    282         double sumB = 0.0;              // Sum of B products (for matrix X, background)
    283         double sumI2 = 0.0;             // Sum of I_2 (for vector 1, background)
    284         double sumI1I2 = 0.0;           // Sum of I_1.I_2 (for vector 1, normalisation)
     382            // Spatial variation of kernel coeffs
     383            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     384                for (int iTerm = 0, iIndex = i; iTerm < numPoly; iTerm++, iIndex += numKernels) {
     385                    for (int jTerm = 0, jIndex = j; jTerm < numPoly; jTerm++, jIndex += numKernels) {
     386                        double ab = sumAB * poly2[iTerm][jTerm];
     387                        matrix->data.F64[iIndex][jIndex + numParams] = ab;
     388                        matrix->data.F64[jIndex + numParams][iIndex] = ab;
     389                    }
     390                }
     391            }
     392        }
     393
     394        double sumAI2 = 0.0;            // Sum of A.I_2 products (for vector)
     395        double sumBI2 = 0.0;            // Sum of B.I_2 products (for vector)
     396        double sumAI1 = 0.0;            // Sum of A.I_1 products (for matrix, normalisation)
     397        double sumA = 0.0;              // Sum of A (for matrix, background)
     398        double sumBI1 = 0.0;            // Sum of B.I_1 products (for matrix, normalisation)
     399        double sumB = 0.0;              // Sum of B products (for matrix, background)
     400        double sumI2 = 0.0;             // Sum of I_2 (for vector, background)
    285401        for (int y = - footprint; y <= footprint; y++) {
    286402            for (int x = - footprint; x <= footprint; x++) {
    287                 float a = iConv1->kernel[y][x];
    288                 float b = iConv2->kernel[y][x];
     403                double a = iConv1->kernel[y][x];
     404                double b = iConv2->kernel[y][x];
    289405                float i1 = image1->kernel[y][x];
    290406                float i2 = image2->kernel[y][x];
     
    294410                double ai1 = a * i1;
    295411                double bi1 = b * i1;
    296                 double i1i2 = i1 * i2;
    297 
    298 #ifdef USE_WEIGHT
    299                 float wtVal = weight->kernel[y][x];
    300                 ai2 *= wtVal;
    301                 bi2 *= wtVal;
    302                 ai1 *= wtVal;
    303                 bi1 *= wtVal;
    304                 i1i2 *= wtVal;
    305                 a *= wtVal;
    306                 b *= wtVal;
    307                 i2 *= wtVal;
    308 #endif
    309 
     412
     413                if (weight) {
     414                    float wtVal = weight->kernel[y][x];
     415                    ai2 *= wtVal;
     416                    bi2 *= wtVal;
     417                    ai1 *= wtVal;
     418                    bi1 *= wtVal;
     419                    a *= wtVal;
     420                    b *= wtVal;
     421                    i2 *= wtVal;
     422                }
     423                if (window) {
     424                    float wtVal = window->kernel[y][x];
     425                    ai2 *= wtVal;
     426                    bi2 *= wtVal;
     427                    ai1 *= wtVal;
     428                    bi1 *= wtVal;
     429                    a *= wtVal;
     430                    b *= wtVal;
     431                    i2 *= wtVal;
     432                }
    310433                sumAI2 += ai2;
    311434                sumBI2 += bi2;
     
    315438                sumB += b;
    316439                sumI2 += i2;
    317                 sumI1I2 += i1i2;
    318440            }
    319441        }
     
    323445            double bi2 = sumBI2 * poly[iTerm];
    324446            double ai1 = sumAI1 * poly[iTerm];
    325             double a = sumA * poly[iTerm];
     447            double a   = sumA * poly[iTerm];
    326448            double bi1 = sumBI1 * poly[iTerm];
    327             double b = sumB * poly[iTerm];
    328 
    329             matrix1->data.F64[iIndex][normIndex] = ai1;
    330             matrix1->data.F64[normIndex][iIndex] = ai1;
    331             matrix1->data.F64[iIndex][bgIndex] = a;
    332             matrix1->data.F64[bgIndex][iIndex] = a;
    333             vector1->data.F64[iIndex] = ai2;
    334             vector2->data.F64[iIndex] = bi2;
    335             matrixX->data.F64[iIndex][normIndex] = bi1;
    336             matrixX->data.F64[iIndex][bgIndex] = b;
    337         }
    338     }
    339 
    340     double sumI1 = 0.0;                 // Sum of I_1 (for matrix 1, background-normalisation)
    341     double sumI1I1 = 0.0;               // Sum of I_1^2 (for matrix 1, normalisation-normalisation)
    342     double sum1 = 0.0;                  // Sum of 1 (for matrix 1, background-background)
    343     double sumI2 = 0.0;                 // Sum of I_2 (for vector 1, background)
    344     double sumI1I2 = 0.0;               // Sum of I_1.I_2 (for vector 1, normalisation)
     449            double b   = sumB * poly[iTerm];
     450
     451            if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     452                matrix->data.F64[iIndex][normIndex] = ai1;
     453                matrix->data.F64[normIndex][iIndex] = ai1;
     454                matrix->data.F64[iIndex + numParams][normIndex] = bi1;
     455                matrix->data.F64[normIndex][iIndex + numParams] = bi1;
     456            }
     457            if ((mode & PM_SUBTRACTION_EQUATION_BG) && (mode & PM_SUBTRACTION_EQUATION_KERNELS)) {
     458                matrix->data.F64[iIndex][bgIndex] = a;
     459                matrix->data.F64[bgIndex][iIndex] = a;
     460                matrix->data.F64[iIndex + numParams][bgIndex] = b;
     461                matrix->data.F64[bgIndex][iIndex + numParams] = b;
     462            }
     463            if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     464                vector->data.F64[iIndex] = ai2;
     465                vector->data.F64[iIndex + numParams] = bi2;
     466                if (!(mode & PM_SUBTRACTION_EQUATION_NORM)) {
     467                    // subtract norm * sumRC * poly[iTerm]
     468                    psAssert (kernels->solution1, "programming error: define solution first!");
     469                    int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     470                    double norm = fabs(kernels->solution1->data.F64[normIndex]);  // Normalisation
     471                    vector->data.F64[iIndex] -= norm * ai1;
     472                    vector->data.F64[iIndex + numParams] -= norm * bi1;
     473                }
     474            }
     475        }
     476    }
     477
     478    double sumI1 = 0.0;                 // Sum of I_1 (for matrix, background-normalisation)
     479    double sumI1I1 = 0.0;               // Sum of I_1^2 (for matrix, normalisation-normalisation)
     480    double sum1 = 0.0;                  // Sum of 1 (for matrix, background-background)
     481    double sumI2 = 0.0;                 // Sum of I_2 (for vector, background)
     482    double sumI1I2 = 0.0;               // Sum of I_1.I_2 (for vector, normalisation)
     483    double normI1 = 0.0, normI2 = 0.0;  // Sum of I_1 and I_2 within the normalisation window
    345484    for (int y = - footprint; y <= footprint; y++) {
    346485        for (int x = - footprint; x <= footprint; x++) {
    347             float i1 = image1->kernel[y][x];
    348             float i2 = image2->kernel[y][x];
     486            double i1 = image1->kernel[y][x];
     487            double i2 = image2->kernel[y][x];
    349488
    350489            double i1i1 = i1 * i1;
     
    352491            double i1i2 = i1 * i2;
    353492
    354 #ifdef USE_WEIGHT
    355             float wtVal = weight->kernel[y][x];
    356             i1 *= wtVal;
    357             i1i1 *= wtVal;
    358             one *= wtVal;
    359             i2 *= wtVal;
    360             i1i2 *= wtVal;
    361 #endif
    362 
     493            if (PS_SQR(x) + PS_SQR(y) <= PS_SQR(normWindow)) {
     494                normI1 += i1;
     495                normI2 += i2;
     496            }
     497
     498            if (weight) {
     499                float wtVal = weight->kernel[y][x];
     500                i1 *= wtVal;
     501                i1i1 *= wtVal;
     502                one *= wtVal;
     503                i2 *= wtVal;
     504                i1i2 *= wtVal;
     505            }
     506            if (window) {
     507                float wtVal = window->kernel[y][x];
     508                i1 *= wtVal;
     509                i1i1 *= wtVal;
     510                one *= wtVal;
     511                i2 *= wtVal;
     512                i1i2 *= wtVal;
     513            }
    363514            sumI1 += i1;
    364515            sumI1I1 += i1i1;
     
    368519        }
    369520    }
    370     matrix1->data.F64[bgIndex][normIndex] = sumI1;
    371     matrix1->data.F64[normIndex][bgIndex] = sumI1;
    372     matrix1->data.F64[normIndex][normIndex] = sumI1I1;
    373     matrix1->data.F64[bgIndex][bgIndex] = sum1;
    374     vector1->data.F64[bgIndex] = sumI2;
    375     vector1->data.F64[normIndex] = sumI1I2;
     521
     522    *norm = normI2 / normI1;
     523
     524    if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     525        matrix->data.F64[normIndex][normIndex] = sumI1I1;
     526        vector->data.F64[normIndex] = sumI1I2;
     527    }
     528    if (mode & PM_SUBTRACTION_EQUATION_BG) {
     529        matrix->data.F64[bgIndex][bgIndex] = sum1;
     530        vector->data.F64[bgIndex] = sumI2;
     531    }
     532    if ((mode & PM_SUBTRACTION_EQUATION_NORM) && (mode & PM_SUBTRACTION_EQUATION_BG)) {
     533        matrix->data.F64[bgIndex][normIndex] = sumI1;
     534        matrix->data.F64[normIndex][bgIndex] = sumI1;
     535    }
     536
     537    // check for any NAN values in the result, skip if found:
     538    for (int iy = 0; iy < matrix->numRows; iy++) {
     539        for (int ix = 0; ix < matrix->numCols; ix++) {
     540            if (!isfinite(matrix->data.F64[iy][ix])) {
     541                fprintf (stderr, "WARNING: NAN in matrix\n");
     542                return false;
     543            }
     544        }
     545    }
     546    for (int ix = 0; ix < vector->n; ix++) {
     547        if (!isfinite(vector->data.F64[ix])) {
     548            fprintf (stderr, "WARNING: NAN in vector\n");
     549            return false;
     550        }
     551    }
     552
    376553
    377554    return true;
    378555}
    379556
    380 // Merge dual matrices and vectors into single matrix equation
    381 // Have: Aa = Ct.b + d
    382 // Have: Ca = Bb + e
    383 // Set: F = ( A -Ct ;  C -B )
    384 // Set: g = ( a ; b )
    385 // Set: h = ( d ; e )
    386 // So that we combine the above two equations: Fg = h
    387 static bool calculateEquationDual(psImage **outMatrix,
    388                                   psVector **outVector,
    389                                   const psImage *sumMatrix1,
    390                                   const psImage *sumMatrix2,
    391                                   const psImage *sumMatrixX,
    392                                   const psVector *sumVector1,
    393                                   const psVector *sumVector2
    394                                   )
    395 {
    396     psAssert(sumMatrix1 && sumMatrix2 && sumMatrixX, "Require input matrices");
    397     psAssert(sumVector1 && sumVector2, "Require input vectors");
    398     int num1 = sumVector1->n;   // Number of parameters in first set
    399     int num2 = sumVector2->n;   // Number of parameters in second set
    400     int num = num1 + num2;      // Number of parameters in new set
    401 
    402     psAssert(sumMatrix1->type.type == PS_TYPE_F64 &&
    403              sumMatrix2->type.type == PS_TYPE_F64 &&
    404              sumMatrixX->type.type == PS_TYPE_F64 &&
    405              sumVector1->type.type == PS_TYPE_F64 &&
    406              sumVector2->type.type == PS_TYPE_F64,
    407              "Require input type is F64");
    408 
    409     psAssert(outMatrix, "Require output matrix");
    410     psAssert(outVector, "Require output vector");
    411     if (!*outMatrix) {
    412         *outMatrix = psImageAlloc(num, num, PS_TYPE_F64);
    413     }
    414     if (!*outVector) {
    415         *outVector = psVectorAlloc(num, PS_TYPE_F64);
    416     }
    417     psImage *matrix = *outMatrix;
    418     psVector *vector = *outVector;
    419 
    420     psAssert(sumMatrix1->numCols == num1 && sumMatrix1->numRows == num1, "Require size NxN");
    421     psAssert(sumMatrix2->numCols == num2 && sumMatrix2->numRows == num2, "Require size MxM");
    422     psAssert(sumMatrixX->numCols == num1 && sumMatrixX->numRows == num2, "Require size MxN");
    423 
    424     memcpy(vector->data.F64, sumVector1->data.F64, num1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    425     memcpy(&vector->data.F64[num1], sumVector2->data.F64, num2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    426 
    427     for (int i = 0; i < num1; i++) {
    428         memcpy(matrix->data.F64[i], sumMatrix1->data.F64[i], num1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    429         for (int j = 0, k = num1; j < num2; j++, k++) {
    430             matrix->data.F64[i][k] = - sumMatrixX->data.F64[j][i];
    431         }
    432     }
    433     for (int i1 = 0, i2 = num1; i1 < num2; i1++, i2++) {
    434         memcpy(matrix->data.F64[i2], sumMatrixX->data.F64[i1], num1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    435         for (int j = 0, k = num1; j < num2; j++, k++) {
    436             matrix->data.F64[i2][k] = - sumMatrix2->data.F64[i1][j];
    437         }
    438     }
    439 
    440     return true;
    441 }
    442 
    443 
     557#if 1
    444558// Add in penalty term to least-squares vector
    445 static bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
     559bool calculatePenalty(psImage *matrix,                     // Matrix to which to add in penalty term
    446560                             psVector *vector,                    // Vector to which to add in penalty term
    447561                             const pmSubtractionKernels *kernels, // Kernel parameters
     
    456570    int spatialOrder = kernels->spatialOrder; // Order of spatial variations
    457571    int numKernels = kernels->num; // Number of kernel components
     572    int numSpatial = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of spatial variations
     573    int numParams = numKernels * numSpatial;                 // Number of kernel parameters
     574
     575    // order is :
     576    // [p_0,x_0,y_0 p_1,x_0,y_0, p_2,x_0,y_0]
     577    // [p_0,x_1,y_0 p_1,x_1,y_0, p_2,x_1,y_0]
     578    // [p_0,x_0,y_1 p_1,x_0,y_1, p_2,x_0,y_1]
     579    // [norm]
     580    // [bg]
     581    // [q_0,x_0,y_0 q_1,x_0,y_0, q_2,x_0,y_0]
     582    // [q_0,x_1,y_0 q_1,x_1,y_0, q_2,x_1,y_0]
     583    // [q_0,x_0,y_1 q_1,x_0,y_1, q_2,x_0,y_1]
     584
    458585    for (int i = 0; i < numKernels; i++) {
    459586        for (int yOrder = 0, index = i; yOrder <= spatialOrder; yOrder++) {
    460587            for (int xOrder = 0; xOrder <= spatialOrder - yOrder; xOrder++, index += numKernels) {
    461588                // Contribution to chi^2: a_i^2 P_i
    462                 matrix->data.F64[index][index] -= norm * penalties->data.F32[i];
     589                psAssert(isfinite(penalties->data.F32[i]), "Invalid penalty");
     590                matrix->data.F64[index][index] += norm * penalties->data.F32[i];
     591                if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     592                    matrix->data.F64[index + numParams + 2][index + numParams + 2] += norm * penalties->data.F32[i];
     593                    // matrix[i][i] is ~ (k_i * I_1)(k_i * I_1)
     594                    // penalties scale with second moments
     595                    //
     596                }
    463597            }
    464598        }
     
    467601    return true;
    468602}
     603# endif
    469604
    470605//////////////////////////////////////////////////////////////////////////////////////////////////////////////
     
    476611// Calculate the value of a polynomial, specified by coefficients and polynomial values
    477612double p_pmSubtractionCalculatePolynomial(const psVector *coeff, // Coefficients
    478                                                  const psImage *polyValues, // Polynomial values
    479                                                  int order, // Order of polynomials
    480                                                  int index, // Index at which to begin
    481                                                  int step // Step between subsequent indices
    482                                                  )
     613                                          const psImage *polyValues, // Polynomial values
     614                                          int order, // Order of polynomials
     615                                          int index, // Index at which to begin
     616                                          int step // Step between subsequent indices
     617                                          )
    483618{
    484619    double sum = 0.0;                   // Value of the polynomial sum
     
    495630
    496631double p_pmSubtractionSolutionCoeff(const pmSubtractionKernels *kernels, const psImage *polyValues,
    497                                            int index, bool wantDual)
     632                                    int index, bool wantDual)
    498633{
    499634#if 0
     
    548683    const pmSubtractionKernels *kernels = job->args->data[1]; // Kernels
    549684    int index = PS_SCALAR_VALUE(job->args->data[2], S32); // Stamp index
    550 
    551     return pmSubtractionCalculateEquationStamp(stamps, kernels, index);
     685    pmSubtractionEquationCalculationMode mode  = PS_SCALAR_VALUE(job->args->data[3], S32); // calculation model
     686
     687    return pmSubtractionCalculateEquationStamp(stamps, kernels, index, mode);
    552688}
    553689
    554690bool pmSubtractionCalculateEquationStamp(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
    555                                          int index)
     691                                         int index, const pmSubtractionEquationCalculationMode mode)
    556692{
    557693    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    566702    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
    567703
     704    // numKernels is the number of unique kernel images (one for each Gaussian modified by a specific polynomial).
     705    // = \sum_i^N_Gaussians [(order + 1) * (order + 2) / 2], eg for 1 Gauss and 1st order, numKernels = 3
     706
    568707    // Total number of parameters to solve for: coefficient of each kernel basis function, multipled by the
    569708    // number of coefficients for the spatial polynomial, normalisation and a constant background offset.
    570709    int numParams = numKernels * numSpatial + 1 + numBackground;
     710    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     711        // An additional image is convolved
     712        numParams += numKernels * numSpatial;
     713    }
    571714
    572715    pmSubtractionStamp *stamp = stamps->stamps->data[index]; // Stamp of interest
    573716    psAssert(stamp->status == PM_SUBTRACTION_STAMP_CALCULATE, "We only operate on stamps with this state.");
    574717
    575     // Generate convolutions
     718    // Generate convolutions: these are generated once and saved
    576719    if (!pmSubtractionConvolveStamp(stamp, kernels, footprint)) {
    577720        psError(PS_ERR_UNKNOWN, false, "Unable to convolve stamp %d.", index);
     
    603746#endif
    604747
     748    // XXX visualize the set of convolved stamps
     749
    605750    psImage *polyValues = p_pmSubtractionPolynomial(NULL, spatialOrder,
    606751                                                    stamp->xNorm, stamp->yNorm); // Polynomial terms
    607752
    608     bool new = stamp->vector1 ? false : true; // Is this a new run?
     753    bool new = stamp->vector ? false : true; // Is this a new run?
    609754    if (new) {
    610         stamp->matrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    611         stamp->vector1 = psVectorAlloc(numParams, PS_TYPE_F64);
     755        stamp->matrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     756        stamp->vector = psVectorAlloc(numParams, PS_TYPE_F64);
    612757    }
    613758#ifdef TESTING
    614     psImageInit(stamp->matrix1, NAN);
    615     psVectorInit(stamp->vector1, NAN);
     759    psImageInit(stamp->matrix, NAN);
     760    psVectorInit(stamp->vector, NAN);
    616761#endif
    617762
    618763    bool status;                    // Status of least-squares matrix/vector calculation
     764
     765    psKernel *weight = NULL;
     766    psKernel *window = NULL;
     767
     768#ifdef USE_WEIGHT
     769    weight = stamp->weight;
     770#endif
     771#ifdef USE_WINDOW
     772    window = stamps->window;
     773#endif
     774
    619775    switch (kernels->mode) {
    620776      case PM_SUBTRACTION_MODE_1:
    621         status = calculateMatrixVector(stamp->matrix1, stamp->vector1, stamp->image2, stamp->image1,
    622                                        stamp->weight, stamp->convolutions1, kernels, polyValues,
    623                                        footprint);
     777        status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image2, stamp->image1,
     778                                       weight, window, stamp->convolutions1, kernels,
     779                                       polyValues, footprint, stamps->normWindow, mode);
    624780        break;
    625781      case PM_SUBTRACTION_MODE_2:
    626         status = calculateMatrixVector(stamp->matrix1, stamp->vector1, stamp->image1, stamp->image2,
    627                                        stamp->weight, stamp->convolutions2, kernels, polyValues,
    628                                        footprint);
     782        status = calculateMatrixVector(stamp->matrix, stamp->vector, &stamp->norm, stamp->image1, stamp->image2,
     783                                       weight, window, stamp->convolutions2, kernels,
     784                                       polyValues, footprint, stamps->normWindow, mode);
    629785        break;
    630786      case PM_SUBTRACTION_MODE_DUAL:
    631         if (new) {
    632             stamp->matrix2 = psImageAlloc(numKernels * numSpatial, numKernels * numSpatial, PS_TYPE_F64);
    633             stamp->matrixX = psImageAlloc(numParams, numKernels * numSpatial, PS_TYPE_F64);
    634             stamp->vector2 = psVectorAlloc(numKernels * numSpatial, PS_TYPE_F64);
    635         }
    636 #ifdef TESTING
    637         psImageInit(stamp->matrix2, NAN);
    638         psImageInit(stamp->matrixX, NAN);
    639         psVectorInit(stamp->vector2, NAN);
    640 #endif
    641         status = calculateDualMatrixVector(stamp->matrix1, stamp->vector1, stamp->matrix2, stamp->vector2,
    642                                            stamp->matrixX, stamp->image1, stamp->image2, stamp->weight,
    643                                            stamp->convolutions1, stamp->convolutions2, kernels, polyValues,
    644                                            footprint);
     787        status = calculateDualMatrixVector(stamp->matrix, stamp->vector, &stamp->norm,
     788                                           stamp->image1, stamp->image2,
     789                                           weight, window, stamp->convolutions1, stamp->convolutions2,
     790                                           kernels, polyValues, footprint, stamps->normWindow, mode);
    645791        break;
    646792      default:
     
    651797        stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    652798        psWarning("Rejecting stamp %d (%d,%d) because of bad equation",
    653                   index, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     799                  index, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    654800    } else {
    655801        stamp->status = PM_SUBTRACTION_STAMP_USED;
     
    659805    {
    660806        psString matrixName = NULL;
    661         psStringAppend(&matrixName, "matrix1_%d.fits", index);
     807        psStringAppend(&matrixName, "matrix_%d.fits", index);
    662808        psFits *matrixFile = psFitsOpen(matrixName, "w");
    663809        psFree(matrixName);
    664         psFitsWriteImage(matrixFile, NULL, stamp->matrix1, 0, NULL);
     810        psFitsWriteImage(matrixFile, NULL, stamp->matrix, 0, NULL);
    665811        psFitsClose(matrixFile);
    666812
    667813        matrixName = NULL;
    668         psStringAppend(&matrixName, "vector1_%d.fits", index);
    669         psImage *dummy = psImageAlloc(stamp->vector1->n, 1, PS_TYPE_F64);
    670         memcpy(dummy->data.F64[0], stamp->vector1->data.F64,
    671                PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector1->n);
     814        psStringAppend(&matrixName, "vector_%d.fits", index);
     815        psImage *dummy = psImageAlloc(stamp->vector->n, 1, PS_TYPE_F64);
     816        memcpy(dummy->data.F64[0], stamp->vector->data.F64,
     817               PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector->n);
    672818        matrixFile = psFitsOpen(matrixName, "w");
    673819        psFree(matrixName);
     
    675821        psFree(dummy);
    676822        psFitsClose(matrixFile);
    677 
    678         if (stamp->vector2) {
    679             matrixName = NULL;
    680             psStringAppend(&matrixName, "vector2_%d.fits", index);
    681             dummy = psImageAlloc(stamp->vector2->n, 1, PS_TYPE_F64);
    682             memcpy(dummy->data.F64[0], stamp->vector2->data.F64,
    683                    PSELEMTYPE_SIZEOF(PS_TYPE_F64) * stamp->vector2->n);
    684             matrixFile = psFitsOpen(matrixName, "w");
    685             psFree(matrixName);
    686             psFitsWriteImage(matrixFile, NULL, dummy, 0, NULL);
    687             psFree(dummy);
    688             psFitsClose(matrixFile);
    689         }
    690 
    691         if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    692             matrixName = NULL;
    693             psStringAppend(&matrixName, "matrix2_%d.fits", index);
    694             matrixFile = psFitsOpen(matrixName, "w");
    695             psFree(matrixName);
    696             psFitsWriteImage(matrixFile, NULL, stamp->matrix2, 0, NULL);
    697             psFitsClose(matrixFile);
    698 
    699             matrixName = NULL;
    700             psStringAppend(&matrixName, "matrixX_%d.fits", index);
    701             matrixFile = psFitsOpen(matrixName, "w");
    702             psFree(matrixName);
    703             psFitsWriteImage(matrixFile, NULL, stamp->matrixX, 0, NULL);
    704             psFitsClose(matrixFile);
    705         }
    706823    }
    707824#endif
     
    712829}
    713830
    714 bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels)
     831bool pmSubtractionCalculateEquation(pmSubtractionStampList *stamps, const pmSubtractionKernels *kernels,
     832                                    const pmSubtractionEquationCalculationMode mode)
    715833{
    716834    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, false);
     
    727845        }
    728846
     847        if ((stamp->x <= 0.0) && (stamp->y <= 0.0)) {
     848            psAbort ("bad stamp");
     849        }
     850        if (!isfinite(stamp->x) && !isfinite(stamp->y)) {
     851            psAbort ("bad stamp");
     852        }
     853
    729854        if (pmSubtractionThreaded()) {
    730855            psThreadJob *job = psThreadJobAlloc("PSMODULES_SUBTRACTION_CALCULATE_EQUATION");
     
    732857            psArrayAdd(job->args, 1, (pmSubtractionKernels*)kernels); // Casting away const to put on array
    733858            PS_ARRAY_ADD_SCALAR(job->args, i, PS_TYPE_S32);
     859            PS_ARRAY_ADD_SCALAR(job->args, mode, PS_TYPE_S32);
    734860            if (!psThreadJobAddPending(job)) {
    735861                psFree(job);
     
    738864            psFree(job);
    739865        } else {
    740             pmSubtractionCalculateEquationStamp(stamps, kernels, i);
     866            pmSubtractionCalculateEquationStamp(stamps, kernels, i, mode);
    741867        }
    742868    }
     
    748874
    749875    pmSubtractionVisualPlotLeastSquares(stamps);
     876    pmSubtractionVisualShowKernels((pmSubtractionKernels  *)kernels);
     877    pmSubtractionVisualShowBasis(stamps);
    750878
    751879    psLogMsg("psModules.imcombine", PS_LOG_INFO, "Calculate equation: %f sec",
     
    756884}
    757885
    758 bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps)
     886// private functions used on pmSubtractionSolveEquation
     887bool psVectorWriteFile (char *filename, const psVector *vector);
     888bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header);
     889
     890psImage *p_pmSubSolve_wUt (psVector *w, psImage *U);
     891psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt);
     892
     893bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask);
     894
     895bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B);
     896bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB);
     897bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB);
     898
     899bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x);
     900bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y);
     901bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
     902
     903psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w);
     904
     905double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps);
     906
     907bool pmSubtractionSolveEquation(pmSubtractionKernels *kernels,
     908                                const pmSubtractionStampList *stamps,
     909                                const pmSubtractionEquationCalculationMode mode)
    759910{
    760911    PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernels, false);
     
    762913
    763914    // Check inputs
    764     int numParams = -1;                // Number of parameters
    765     int numParams2 = 0;                // Number of parameters for part solution (DUAL mode)
     915    int numKernels = kernels->num;      // Number of kernel basis functions
     916    int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations
     917    int numBackground = PM_SUBTRACTION_POLYTERMS(kernels->bgOrder); // Number of background terms
     918    int numParams = numKernels * numSpatial + 1 + numBackground;    // Number of parameters being solved for
     919    int numSolution1 = numParams, numSolution2 = 0;                 // Number of parameters for each solution
     920    if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
     921        // An additional image is convolved
     922        numSolution2 = numKernels * numSpatial;
     923        numParams += numSolution2;
     924    }
     925
    766926    for (int i = 0; i < stamps->num; i++) {
    767927        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
     
    771931        }
    772932
    773         PS_ASSERT_VECTOR_NON_NULL(stamp->vector1, false);
    774         if (numParams == -1) {
    775             numParams = stamp->vector1->n;
    776         }
    777         PS_ASSERT_VECTOR_SIZE(stamp->vector1, (long)numParams, false);
    778         PS_ASSERT_VECTOR_TYPE(stamp->vector1, PS_TYPE_F64, false);
    779         PS_ASSERT_IMAGE_NON_NULL(stamp->matrix1, false);
    780         PS_ASSERT_IMAGE_SIZE(stamp->matrix1, numParams, numParams, false);
    781         PS_ASSERT_IMAGE_TYPE(stamp->matrix1, PS_TYPE_F64, false);
    782 
    783         if (kernels->mode == PM_SUBTRACTION_MODE_DUAL) {
    784             PS_ASSERT_IMAGE_NON_NULL(stamp->matrix2, false);
    785             PS_ASSERT_IMAGE_NON_NULL(stamp->matrixX, false);
    786             if (numParams2 == 0) {
    787                 numParams2 = stamp->matrix2->numCols;
    788             }
    789             PS_ASSERT_IMAGE_SIZE(stamp->matrix2, numParams2, numParams2, false);
    790             PS_ASSERT_IMAGE_SIZE(stamp->matrixX, numParams, numParams2, false);
    791             PS_ASSERT_IMAGE_TYPE(stamp->matrix2, PS_TYPE_F64, false);
    792             PS_ASSERT_IMAGE_TYPE(stamp->matrixX, PS_TYPE_F64, false);
    793             PS_ASSERT_VECTOR_NON_NULL(stamp->vector2, false);
    794             PS_ASSERT_VECTOR_SIZE(stamp->vector2, (long)numParams2, false);
    795             PS_ASSERT_VECTOR_TYPE(stamp->vector2, PS_TYPE_F64, false);
    796         }
    797     }
    798     if (numParams == -1) {
    799         psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No suitable stamps found.");
    800         return NULL;
     933        PS_ASSERT_VECTOR_NON_NULL(stamp->vector, false);
     934        PS_ASSERT_VECTOR_SIZE(stamp->vector, (long)numParams, false);
     935        PS_ASSERT_VECTOR_TYPE(stamp->vector, PS_TYPE_F64, false);
     936        PS_ASSERT_IMAGE_NON_NULL(stamp->matrix, false);
     937        PS_ASSERT_IMAGE_SIZE(stamp->matrix, numParams, numParams, false);
     938        PS_ASSERT_IMAGE_TYPE(stamp->matrix, PS_TYPE_F64, false);
    801939    }
    802940
     
    814952        psVectorInit(sumVector, 0.0);
    815953        psImageInit(sumMatrix, 0.0);
     954
     955        psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
     956
    816957        int numStamps = 0;              // Number of good stamps
    817958        for (int i = 0; i < stamps->num; i++) {
    818959            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    819 
    820960            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    821 
    822 #ifdef TESTING
    823               // XXX double-check for NAN in data:
    824                 for (int iy = 0; iy < stamp->matrix1->numRows; iy++) {
    825                     for (int ix = 0; ix < stamp->matrix1->numCols; ix++) {
    826                         if (!isfinite(stamp->matrix1->data.F64[iy][ix])) {
    827                             fprintf (stderr, "WARNING: NAN in matrix1\n");
    828                         }
    829                     }
    830                 }
    831                 for (int ix = 0; ix < stamp->vector1->n; ix++) {
    832                     if (!isfinite(stamp->vector1->data.F64[ix])) {
    833                         fprintf (stderr, "WARNING: NAN in vector1\n");
    834                     }
    835                 }
    836 #endif
    837 
    838                 (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix1);
    839                 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector1);
     961                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     962                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     963                psVectorAppend(norms, stamp->norm);
    840964                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    841965                numStamps++;
     
    845969        }
    846970
    847 #ifdef TESTING
    848         for (int ix = 0; ix < sumVector->n; ix++) {
    849             if (!isfinite(sumVector->data.F64[ix])) {
    850                 fprintf (stderr, "WARNING: NAN in vector1\n");
    851             }
    852         }
    853 #endif
    854 
    855971#if 0
    856972        int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     
    858974#endif
    859975
    860 #ifdef TESTING
    861         for (int ix = 0; ix < sumVector->n; ix++) {
    862             if (!isfinite(sumVector->data.F64[ix])) {
    863                 fprintf (stderr, "WARNING: NAN in vector1\n");
    864             }
    865         }
     976        psVector *solution = NULL;                       // Solution to equation!
     977        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     978        psVectorInit(solution, 0);
     979
     980#if 0
     981        // Regular, straight-forward solution
     982        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     983#else
    866984        {
    867             psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
    868             psFits *fits = psFitsOpen("matrixInv.fits", "w");
    869             psFitsWriteImage(fits, NULL, inverse, 0, NULL);
    870             psFitsClose(fits);
    871             psFree(inverse);
    872         }
    873         {
    874             psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
    875             psImage *Xt = psMatrixTranspose(NULL, X);
    876             psImage *XtX = psMatrixMultiply(NULL, Xt, X);
    877             psFits *fits = psFitsOpen("matrixErr.fits", "w");
    878             psFitsWriteImage(fits, NULL, XtX, 0, NULL);
    879             psFitsClose(fits);
    880             psFree(X);
    881             psFree(Xt);
    882             psFree(XtX);
    883         }
    884 #endif
    885 
    886         psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
    887         psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
     985            // Solve normalisation and background separately
     986            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     987            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     988
     989            psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     990            if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     991                psError(PS_ERR_UNKNOWN, false, "Unable to determine median normalisation");
     992                psFree(stats);
     993                psFree(sumMatrix);
     994                psFree(sumVector);
     995                psFree(norms);
     996                return false;
     997            }
     998
     999            double normValue = stats->robustMedian;
     1000            // double bgValue = 0.0;
     1001
     1002            psFree(stats);
     1003
     1004            fprintf(stderr, "Norm: %lf\n", normValue);
     1005
     1006            // Solve kernel components
     1007            for (int i = 0; i < numSolution1; i++) {
     1008                sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
     1009
     1010                sumMatrix->data.F64[i][normIndex] = 0.0;
     1011                sumMatrix->data.F64[normIndex][i] = 0.0;
     1012            }
     1013            sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
     1014            sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
     1015            sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
     1016
     1017            sumMatrix->data.F64[normIndex][normIndex] = 1.0;
     1018            sumVector->data.F64[normIndex] = 0.0;
     1019
     1020            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1021
     1022            solution->data.F64[normIndex] = normValue;
     1023        }
     1024# endif
     1025
     1026        if (!kernels->solution1) {
     1027            kernels->solution1 = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     1028            psVectorInit(kernels->solution1, 0.0);
     1029        }
     1030
     1031        // only update the solutions that we chose to calculate:
     1032        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     1033            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1034            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     1035        }
     1036        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     1037            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1038            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     1039        }
     1040        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     1041            int numKernels = kernels->num;
     1042            int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     1043            int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     1044            for (int i = 0; i < numKernels * numPoly; i++) {
     1045                kernels->solution1->data.F64[i] = solution->data.F64[i];
     1046            }
     1047        }
     1048
     1049        psFree(solution);
     1050        psFree(sumVector);
    8881051        psFree(sumMatrix);
    889         if (!luMatrix) {
    890             psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n");
    891             psFree(sumVector);
    892             psFree(luMatrix);
    893             psFree(permutation);
    894             return NULL;
    895         }
    896         kernels->solution1 = psMatrixLUSolution(kernels->solution1, luMatrix, sumVector, permutation);
    8971052
    8981053#ifdef TESTING
     
    9001055        for (int ix = 0; ix < kernels->solution1->n; ix++) {
    9011056            if (!isfinite(kernels->solution1->data.F64[ix])) {
    902                 fprintf (stderr, "WARNING: NAN in vector1\n");
    903             }
    904         }
    905 #endif
    906 
    907         psFree(sumVector);
    908         psFree(luMatrix);
    909         psFree(permutation);
    910         if (!kernels->solution1) {
    911             psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n");
    912             return NULL;
    913         }
     1057                fprintf (stderr, "WARNING: NAN in vector\n");
     1058            }
     1059        }
     1060#endif
     1061
    9141062    } else {
    9151063        // Dual convolution solution
    9161064
    9171065        // Accumulation of stamp matrices/vectors
    918         psImage *sumMatrix1 = psImageAlloc(numParams, numParams, PS_TYPE_F64);
    919         psImage *sumMatrix2 = psImageAlloc(numParams2, numParams2, PS_TYPE_F64);
    920         psImage *sumMatrixX = psImageAlloc(numParams, numParams2, PS_TYPE_F64);
    921         psVector *sumVector1 = psVectorAlloc(numParams, PS_TYPE_F64);
    922         psVector *sumVector2 = psVectorAlloc(numParams, PS_TYPE_F64);
    923         psImageInit(sumMatrix1, 0.0);
    924         psImageInit(sumMatrix2, 0.0);
    925         psImageInit(sumMatrixX, 0.0);
    926         psVectorInit(sumVector1, 0.0);
    927         psVectorInit(sumVector2, 0.0);
     1066        psImage *sumMatrix = psImageAlloc(numParams, numParams, PS_TYPE_F64);
     1067        psVector *sumVector = psVectorAlloc(numParams, PS_TYPE_F64);
     1068        psImageInit(sumMatrix, 0.0);
     1069        psVectorInit(sumVector, 0.0);
     1070
     1071        psVector *norms = psVectorAllocEmpty(stamps->num, PS_TYPE_F64); // Normalisations
    9281072
    9291073        int numStamps = 0;              // Number of good stamps
     
    9311075            pmSubtractionStamp *stamp = stamps->stamps->data[i]; // Stamp of interest
    9321076            if (stamp->status == PM_SUBTRACTION_STAMP_USED) {
    933                 (void)psBinaryOp(sumMatrix1, sumMatrix1, "+", stamp->matrix1);
    934                 (void)psBinaryOp(sumMatrix2, sumMatrix2, "+", stamp->matrix2);
    935                 (void)psBinaryOp(sumMatrixX, sumMatrixX, "+", stamp->matrixX);
    936                 (void)psBinaryOp(sumVector1, sumVector1, "+", stamp->vector1);
    937                 (void)psBinaryOp(sumVector2, sumVector2, "+", stamp->vector2);
     1077                (void)psBinaryOp(sumMatrix, sumMatrix, "+", stamp->matrix);
     1078                (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
     1079
     1080                psVectorAppend(norms, stamp->norm);
     1081
    9381082                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
    9391083                numStamps++;
     
    9411085        }
    9421086
    943         int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
    944         calculatePenalty(sumMatrix1, sumVector1, kernels, sumMatrix1->data.F64[bgIndex][bgIndex]);
    945         calculatePenalty(sumMatrix2, sumVector2, kernels, -sumMatrix1->data.F64[bgIndex][bgIndex]);
    946 
    947         psImage *sumMatrix = NULL;      // Combined matrix
    948         psVector *sumVector = NULL;     // Combined vector
    949         calculateEquationDual(&sumMatrix, &sumVector, sumMatrix1, sumMatrix2,
    950                               sumMatrixX, sumVector1, sumVector2);
    951 
    9521087#ifdef TESTING
     1088        psFitsWriteImageSimple ("sumMatrix.fits", sumMatrix, NULL);
     1089        psVectorWriteFile("sumVector.dat", sumVector);
     1090#endif
     1091
     1092#if 1
     1093        // int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1094        // calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[bgIndex][bgIndex]);
     1095
     1096        int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1097        calculatePenalty(sumMatrix, sumVector, kernels, sumMatrix->data.F64[normIndex][normIndex] / 1000.0);
     1098#endif
     1099
     1100        psVector *solution = NULL;                       // Solution to equation!
     1101        solution = psVectorAlloc(numParams, PS_TYPE_F64);
     1102        psVectorInit(solution, 0);
     1103
     1104#if 0
     1105        // Regular, straight-forward solution
     1106        solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1107#else
    9531108        {
    954             psFits *fits = psFitsOpen("sumMatrix.fits", "w");
    955             psFitsWriteImage(fits, NULL, sumMatrix, 0, NULL);
    956             psFitsClose(fits);
    957         }
    958         {
    959             psImage *image = psImageAlloc(1, numParams + numParams2, PS_TYPE_F64);
    960             psFits *fits = psFitsOpen("sumVector.fits", "w");
    961             for (int i = 0; i < numParams + numParams2; i++) {
    962                 image->data.F64[0][i] = sumVector->data.F64[i];
    963             }
    964             psFitsWriteImage(fits, NULL, image, 0, NULL);
    965             psFree(image);
    966             psFitsClose(fits);
    967         }
    968 #endif
    969 
    970         psVector *solution = NULL;                       // Solution to equation!
    971         {
    972             solution = psMatrixSolveSVD(solution, sumMatrix, sumVector);
    973             if (!solution) {
    974                 psError(PS_ERR_UNKNOWN, false, "SVD solution of least-squares equation failed.\n");
     1109            // Solve normalisation and background separately
     1110            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1111            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index for background
     1112
     1113#if 0
     1114            psImage *normMatrix = psImageAlloc(2, 2, PS_TYPE_F64);
     1115            psVector *normVector = psVectorAlloc(2, PS_TYPE_F64);
     1116
     1117            normMatrix->data.F64[0][0] = sumMatrix->data.F64[normIndex][normIndex];
     1118            normMatrix->data.F64[1][1] = sumMatrix->data.F64[bgIndex][bgIndex];
     1119            normMatrix->data.F64[0][1] = normMatrix->data.F64[1][0] = sumMatrix->data.F64[normIndex][bgIndex];
     1120
     1121            normVector->data.F64[0] = sumVector->data.F64[normIndex];
     1122            normVector->data.F64[1] = sumVector->data.F64[bgIndex];
     1123
     1124            psVector *normSolution = psMatrixSolveSVD(NULL, normMatrix, normVector, NAN);
     1125
     1126            double normValue = normSolution->data.F64[0];
     1127            double bgValue = normSolution->data.F64[1];
     1128
     1129            psFree(normMatrix);
     1130            psFree(normVector);
     1131            psFree(normSolution);
     1132#endif
     1133
     1134            psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN); // Statistics for norm
     1135            if (!psVectorStats(stats, norms, NULL, NULL, 0)) {
     1136                psError(PS_ERR_UNKNOWN, false, "Unable to determine median normalisation");
     1137                psFree(stats);
    9751138                psFree(sumMatrix);
    9761139                psFree(sumVector);
    977                 return NULL;
    978             }
    979 
    980             int numSpatial = PM_SUBTRACTION_POLYTERMS(kernels->spatialOrder); // Number of spatial variations
    981             int numKernels = kernels->num; // Number of kernel basis functions
    982 
    983             // Remove a kernel basis for image 1 from the equation
    984 #define MASK_BASIS_1(INDEX)                                             \
    985             {                                                           \
    986                 for (int j = 0, index = INDEX; j < numSpatial; j++, index += numKernels) { \
    987                     for (int k = 0; k < numParams2; k++) {              \
    988                         sumMatrix1->data.F64[k][index] = 0.0;           \
    989                         sumMatrix1->data.F64[index][k] = 0.0;           \
    990                         sumMatrixX->data.F64[k][index] = 0.0;           \
    991                     }                                                   \
    992                     sumMatrix1->data.F64[bgIndex][index] = 0.0;         \
    993                     sumMatrix1->data.F64[index][bgIndex] = 0.0;         \
    994                     sumMatrix1->data.F64[normIndex][index] = 0.0;       \
    995                     sumMatrix1->data.F64[index][normIndex] = 0.0;       \
    996                     sumMatrix1->data.F64[index][index] = 1.0;           \
    997                     sumVector1->data.F64[index] = 0.0;                  \
    998                 }                                                       \
    999             }
    1000 
    1001             // Remove a kernel basis for image 2 from the equation
    1002 #define MASK_BASIS_2(INDEX)                                             \
    1003             {                                                           \
    1004                 for (int j = 0, index = INDEX; j < numSpatial; j++, index += numKernels) { \
    1005                     for (int k = 0; k < numParams2; k++) {              \
    1006                         sumMatrix2->data.F64[k][index] = 0.0;           \
    1007                         sumMatrix2->data.F64[index][k] = 0.0;           \
    1008                         sumMatrixX->data.F64[index][k] = 0.0;           \
    1009                     }                                                   \
    1010                     sumMatrix2->data.F64[index][index] = 1.0;           \
    1011                     sumMatrixX->data.F64[index][normIndex] = 0.0;       \
    1012                     sumMatrixX->data.F64[index][bgIndex] = 0.0;         \
    1013                     sumVector2->data.F64[index] = 0.0;                  \
    1014                 }                                                       \
    1015             }
    1016 
    1017             #define TOL 1.0e-5
    1018             int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
    1019             double norm = fabs(solution->data.F64[normIndex]);  // Normalisation
    1020             double thresh = norm * TOL;                         // Threshold for low parameters
    1021             for (int i = 0; i < numKernels; i++) {
    1022                 // Getting 0th order parameter value.  In the presence of spatial variation, the actual value
    1023                 // of the parameter will vary over the image.  We are in effect getting the value in the
    1024                 // centre of the image.  If we use different polynomial functions (e.g., Chebyshev), we may
    1025                 // have to change this to properly determine the value of the parameter at the centre.
    1026                 double param1 = solution->data.F64[i],
    1027                     param2 = solution->data.F64[numParams + i]; // Parameters of interest
    1028                 bool mask1 = false, mask2 = false;              // Masked the parameter?
    1029                 if (fabs(param1) < thresh) {
    1030                     psTrace("psModules.imcombine", 7, "Parameter %d: 1 below threshold\n", i);
    1031                     MASK_BASIS_1(i);
    1032                     mask1 = true;
    1033                 }
    1034                 if (fabs(param2) < thresh) {
    1035                     psTrace("psModules.imcombine", 7, "Parameter %d: 2 below threshold\n", i);
    1036                     MASK_BASIS_2(i);
    1037                     mask2 = true;
    1038                 }
    1039 
    1040                 if (!mask1 && !mask2) {
    1041                     if (fabs(param1) < fabs(param2)) {
    1042                         psTrace("psModules.imcombine", 7, "Parameter %d: 1 < 2\n", i);
    1043                         MASK_BASIS_1(i);
    1044                     } else {
    1045                         psTrace("psModules.imcombine", 7, "Parameter %d: 2 < 1\n", i);
    1046                         MASK_BASIS_2(i);
    1047                     }
    1048                 }
    1049             }
    1050         }
    1051 
    1052         calculateEquationDual(&sumMatrix, &sumVector, sumMatrix1, sumMatrix2,
    1053                               sumMatrixX, sumVector1, sumVector2);
     1140                psFree(norms);
     1141                return false;
     1142            }
     1143
     1144            double normValue = stats->robustMedian;
     1145
     1146            psFree(stats);
     1147
     1148            fprintf(stderr, "Norm: %lf\n", normValue);
     1149
     1150            // Solve kernel components
     1151            for (int i = 0; i < numSolution2; i++) {
     1152                sumVector->data.F64[i] -= normValue * sumMatrix->data.F64[normIndex][i];
     1153                sumVector->data.F64[i + numSolution1] -= normValue * sumMatrix->data.F64[normIndex][i + numSolution1];
     1154
     1155                sumMatrix->data.F64[i][normIndex] = 0.0;
     1156                sumMatrix->data.F64[normIndex][i] = 0.0;
     1157
     1158                sumMatrix->data.F64[i + numSolution1][normIndex] = 0.0;
     1159                sumMatrix->data.F64[normIndex][i + numSolution1] = 0.0;
     1160            }
     1161            sumVector->data.F64[bgIndex] -= normValue * sumMatrix->data.F64[normIndex][bgIndex];
     1162            sumMatrix->data.F64[bgIndex][normIndex] = 0.0;
     1163            sumMatrix->data.F64[normIndex][bgIndex] = 0.0;
     1164
     1165            sumMatrix->data.F64[normIndex][normIndex] = 1.0;
     1166
     1167            sumVector->data.F64[normIndex] = 0.0;
     1168
     1169            solution = psMatrixSolveSVD(solution, sumMatrix, sumVector, NAN);
     1170
     1171            solution->data.F64[normIndex] = normValue;
     1172        }
     1173#endif
     1174
    10541175
    10551176#ifdef TESTING
    1056         {
    1057             psFits *fits = psFitsOpen("sumMatrixFix.fits", "w");
    1058             psFitsWriteImage(fits, NULL, sumMatrix, 0, NULL);
    1059             psFitsClose(fits);
    1060         }
    1061         {
    1062             psImage *image = psImageAlloc(1, numParams + numParams2, PS_TYPE_F64);
    1063             psFits *fits = psFitsOpen("sumVectorFix.fits", "w");
    1064             for (int i = 0; i < numParams + numParams2; i++) {
    1065                 image->data.F64[0][i] = sumVector->data.F64[i];
    1066             }
    1067             psFitsWriteImage(fits, NULL, image, 0, NULL);
    1068             psFree(image);
    1069             psFitsClose(fits);
    1070         }
    1071 #endif
    1072 
    1073         solution = psMatrixSolveSVD(solution, sumMatrix, sumVector);
    1074         if (!solution) {
    1075             psError(PS_ERR_UNKNOWN, false, "SVD solution of least-squares equation failed.\n");
    1076             psFree(sumMatrix);
    1077             psFree(sumVector);
    1078             return NULL;
    1079         }
    1080 
    1081         psFree(sumMatrix1);
    1082         psFree(sumMatrix2);
    1083         psFree(sumMatrixX);
    1084         psFree(sumVector1);
    1085         psFree(sumVector2);
     1177        for (int i = 0; i < solution->n; i++) {
     1178            fprintf(stderr, "Dual solution %d: %lf\n", i, solution->data.F64[i]);
     1179        }
     1180#endif
    10861181
    10871182        psFree(sumMatrix);
    10881183        psFree(sumVector);
    10891184
    1090 #ifdef TESTING
    1091         {
    1092             psImage *image = psImageAlloc(1, numParams + numParams2, PS_TYPE_F64);
    1093             psFits *fits = psFitsOpen("solnVector.fits", "w");
    1094             for (int i = 0; i < numParams + numParams2; i++) {
    1095                 image->data.F64[0][i] = solution->data.F64[i];
    1096             }
    1097             psFitsWriteImage(fits, NULL, image, 0, NULL);
    1098             psFree(image);
    1099             psFitsClose(fits);
    1100         }
    1101 #endif
     1185        psFree(norms);
    11021186
    11031187        if (!kernels->solution1) {
    1104             kernels->solution1 = psVectorAlloc(numParams, PS_TYPE_F64);
     1188            kernels->solution1 = psVectorAlloc(numSolution1, PS_TYPE_F64);
     1189            psVectorInit (kernels->solution1, 0.0);
    11051190        }
    11061191        if (!kernels->solution2) {
    1107             kernels->solution2 = psVectorAlloc(numParams2, PS_TYPE_F64);
    1108         }
    1109 
    1110         memcpy(kernels->solution1->data.F64, solution->data.F64, numParams * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    1111         memcpy(kernels->solution2->data.F64, &solution->data.F64[numParams],
    1112                numParams2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     1192            kernels->solution2 = psVectorAlloc(numSolution2, PS_TYPE_F64);
     1193            psVectorInit (kernels->solution2, 0.0);
     1194        }
     1195
     1196        // only update the solutions that we chose to calculate:
     1197        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     1198            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     1199            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     1200        }
     1201        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     1202            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1203            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     1204        }
     1205        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     1206            int numKernels = kernels->num;
     1207            for (int i = 0; i < numKernels * numSpatial; i++) {
     1208                // XXX fprintf (stderr, "keep\n");
     1209                kernels->solution1->data.F64[i] = solution->data.F64[i];
     1210                kernels->solution2->data.F64[i] = solution->data.F64[i + numSolution1];
     1211            }
     1212        }
     1213
     1214
     1215        memcpy(kernels->solution1->data.F64, solution->data.F64,
     1216               numSolution1 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
     1217        memcpy(kernels->solution2->data.F64, &solution->data.F64[numSolution1],
     1218               numSolution2 * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
    11131219
    11141220        psFree(solution);
     
    11311237     }
    11321238
    1133     pmSubtractionVisualPlotLeastSquares((pmSubtractionStampList *) stamps); //casting away const
     1239    // pmSubtractionVisualPlotLeastSquares((pmSubtractionStampList *) stamps); //casting away const
    11341240    return true;
    11351241}
    11361242
     1243bool pmSubtractionResidualStats(psVector *fSigRes, psVector *fMaxRes, psVector *fMinRes, psKernel *target, psKernel *source, psKernel *residual, double norm, int footprint) {
     1244
     1245    // XXX measure some useful stats on the residuals
     1246    float sum = 0.0;
     1247    float peak = 0.0;
     1248    for (int y = - footprint; y <= footprint; y++) {
     1249        for (int x = - footprint; x <= footprint; x++) {
     1250            sum += 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
     1251            peak = PS_MAX(peak, 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm));
     1252        }
     1253    }
     1254
     1255    // only count pixels with more than X% of the source flux
     1256    // calculate stdev(dflux)
     1257    float dflux1 = 0.0;
     1258    float dflux2 = 0.0;
     1259    int npix = 0;
     1260
     1261    float dmax = 0.0;
     1262    float dmin = 0.0;
     1263
     1264    for (int y = - footprint; y <= footprint; y++) {
     1265        for (int x = - footprint; x <= footprint; x++) {
     1266            float dflux = 0.5*(target->kernel[y][x] + source->kernel[y][x] * norm);
     1267            if (dflux < 0.02*sum) continue;
     1268            dflux1 += residual->kernel[y][x];
     1269            dflux2 += PS_SQR(residual->kernel[y][x]);
     1270            dmax = PS_MAX(residual->kernel[y][x], dmax);
     1271            dmin = PS_MIN(residual->kernel[y][x], dmin);
     1272            npix ++;
     1273        }
     1274    }
     1275    float sigma = sqrt(dflux2 / npix - PS_SQR(dflux1/npix));
     1276    if (!isfinite(sum))  return false;
     1277    if (!isfinite(dmax)) return false;
     1278    if (!isfinite(dmin)) return false;
     1279    if (!isfinite(peak)) return false;
     1280
     1281    // fprintf (stderr, "sum: %f, peak: %f, sigma: %f, fsigma: %f, fmax: %f, fmin: %f\n", sum, peak, sigma, sigma/sum, dmax/peak, dmin/peak);
     1282    psVectorAppend(fSigRes, sigma/sum);
     1283    psVectorAppend(fMaxRes, dmax/peak);
     1284    psVectorAppend(fMinRes, dmin/peak);
     1285    return true;
     1286}
     1287
    11371288psVector *pmSubtractionCalculateDeviations(pmSubtractionStampList *stamps,
    1138                                            const pmSubtractionKernels *kernels)
     1289                                           pmSubtractionKernels *kernels)
    11391290{
    11401291    PM_ASSERT_SUBTRACTION_STAMP_LIST_NON_NULL(stamps, NULL);
     
    11511302    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
    11521303
     1304    // set up holding images for the visualization
     1305    pmSubtractionVisualShowFitInit (stamps);
     1306
     1307    psVector *fSigRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1308    psVector *fMinRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1309    psVector *fMaxRes = psVectorAllocEmpty(stamps->num, PS_TYPE_F32);
     1310
     1311    // we want to save the residual images for the 9 brightest stamps.
     1312    // identify the 9 brightest stamps
     1313    psVector *keepStamps  = psVectorAlloc(stamps->num, PS_TYPE_S32);
     1314    psVectorInit (keepStamps, 0);
     1315    {
     1316        psVector *flux  = psVectorAlloc(stamps->num, PS_TYPE_F32);
     1317        psVectorInit (flux, 0.0);
     1318
     1319        for (int i = 0; i < stamps->num; i++) {
     1320            pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1321            if (!isfinite(stamp->flux)) continue;
     1322            flux->data.F32[i] = stamp->flux;
     1323        }
     1324
     1325        psVector *index = psVectorSortIndex(NULL, flux);
     1326        for (int i = 0; (i < stamps->num) && (i < 9); i++) {
     1327            int n = stamps->num - i - 1;
     1328            keepStamps->data.S32[index->data.S32[n]] = 1;
     1329            fprintf (stderr, "keeping %d (%d of %d)\n", index->data.S32[n], n, 9);
     1330        }
     1331        psFree (flux);
     1332        psFree (index);
     1333
     1334        // this function is called multiple times in the iteration, but
     1335        // we only know after the interation is done if we will try again.
     1336        // therefore we must save the sample each time, and blow away the old one
     1337        // if it exists.
     1338        psFree (kernels->sampleStamps);
     1339        kernels->sampleStamps = psArrayAllocEmpty(9);
     1340    }
     1341
     1342    psString log = psStringCopy("Deviations:\n");               // Log message with deviations
    11531343    for (int i = 0; i < stamps->num; i++) {
    11541344        pmSubtractionStamp *stamp = stamps->stamps->data[i]; // The stamp of interest
     
    12101400                for (int y = - footprint; y <= footprint; y++) {
    12111401                    for (int x = - footprint; x <= footprint; x++) {
    1212                         residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
     1402                        residual->kernel[y][x] += convolution->kernel[y][x] * coefficient;
    12131403                    }
    12141404                }
    12151405            }
     1406
     1407            // XXX visualize the target, source, convolution and residual
     1408            pmSubtractionVisualShowFitAddStamp (target, source, residual, background, norm, i);
     1409
    12161410            for (int y = - footprint; y <= footprint; y++) {
    12171411                for (int x = - footprint; x <= footprint; x++) {
    1218                     residual->kernel[y][x] += target->kernel[y][x] - background - source->kernel[y][x] * norm;
    1219                 }
    1220             }
     1412                    residual->kernel[y][x] += background + source->kernel[y][x] * norm - target->kernel[y][x];
     1413                }
     1414            }
     1415
     1416            if (keepStamps->data.S32[i]) {
     1417                psImage *sample = psImageCopy(NULL, residual->image, PS_TYPE_F32);
     1418                psArrayAdd (kernels->sampleStamps, 9, sample);
     1419                psFree (sample);
     1420            }
     1421
     1422            pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, target, source, residual, norm, footprint);
     1423
    12211424        } else {
    12221425            // Dual convolution
     
    12341437                for (int y = - footprint; y <= footprint; y++) {
    12351438                    for (int x = - footprint; x <= footprint; x++) {
    1236                         residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 - conv1->kernel[y][x] * coeff1;
     1439                        residual->kernel[y][x] += conv2->kernel[y][x] * coeff2 + conv1->kernel[y][x] * coeff1;
    12371440                    }
    12381441                }
    12391442            }
     1443
     1444            // XXX visualize the target, source, convolution and residual
     1445            pmSubtractionVisualShowFitAddStamp (image2, image1, residual, background, norm, i);
     1446
    12401447            for (int y = - footprint; y <= footprint; y++) {
    12411448                for (int x = - footprint; x <= footprint; x++) {
    1242                     residual->kernel[y][x] += image2->kernel[y][x] - background - image1->kernel[y][x] * norm;
    1243                 }
    1244             }
     1449                    residual->kernel[y][x] += background + image1->kernel[y][x] * norm - image2->kernel[y][x];
     1450                }
     1451            }
     1452            if (keepStamps->data.S32[i]) {
     1453                psImage *sample = psImageCopy(NULL, residual->image, PS_TYPE_F32);
     1454                psArrayAdd (kernels->sampleStamps, 9, sample);
     1455                psFree (sample);
     1456            }
     1457
     1458            pmSubtractionResidualStats(fSigRes, fMaxRes, fMinRes, image1, image2, residual, norm, footprint);
    12451459        }
    12461460
     
    12571471        deviations->data.F32[i] = devNorm * deviation;
    12581472        psTrace("psModules.imcombine", 5, "Deviation for stamp %d (%d,%d): %f\n",
    1259                 i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5), deviations->data.F32[i]);
     1473                i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
     1474        psStringAppend(&log, "Stamp %d (%d,%d): %f\n",
     1475                       i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5), deviations->data.F32[i]);
    12601476        if (!isfinite(deviations->data.F32[i])) {
    12611477            stamp->status = PM_SUBTRACTION_STAMP_REJECTED;
    12621478            psTrace("psModules.imcombine", 5,
    12631479                    "Rejecting stamp %d (%d,%d) because of non-finite deviation\n",
    1264                     i, (int)(stamp->x + 0.5), (int)(stamp->y + 0.5));
     1480                    i, (int)(stamp->x - 0.5), (int)(stamp->y - 0.5));
    12651481            continue;
    12661482        }
     
    13021518
    13031519    }
     1520
     1521    psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "%s", log);
     1522    psFree(log);
     1523
     1524    // calculate and report the normalization and background for the image center
     1525    {
     1526        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, 0.0, 0.0);
     1527        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     1528        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
     1529        psLogMsg("psModules.imcombine", PS_LOG_INFO, "normalization: %f, background: %f", norm, background);
     1530
     1531        pmSubtractionVisualShowFit(norm);
     1532        pmSubtractionVisualPlotFit(kernels);
     1533
     1534        psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV);
     1535        psVectorStats (stats, fSigRes, NULL, NULL, 0);
     1536        kernels->fSigResMean = stats->robustMedian;
     1537        kernels->fSigResStdev = stats->robustStdev;
     1538
     1539        psStatsInit (stats);
     1540        psVectorStats (stats, fMaxRes, NULL, NULL, 0);
     1541        kernels->fMaxResMean = stats->robustMedian;
     1542        kernels->fMaxResStdev = stats->robustStdev;
     1543
     1544        psStatsInit (stats);
     1545        psVectorStats (stats, fMinRes, NULL, NULL, 0);
     1546        kernels->fMinResMean = stats->robustMedian;
     1547        kernels->fMinResStdev = stats->robustStdev;
     1548
     1549        // XXX save these values somewhere
     1550        psLogMsg("psModules.imcombine", PS_LOG_INFO, "fSigma: %f +/- %f, fMaxRes: %f +/- %f, fMinRes: %f +/- %f",
     1551                 kernels->fSigResMean, kernels->fSigResStdev,
     1552                 kernels->fMaxResMean, kernels->fMaxResStdev,
     1553                 kernels->fMinResMean, kernels->fMinResStdev);
     1554
     1555        psFree (fSigRes);
     1556        psFree (fMaxRes);
     1557        psFree (fMinRes);
     1558        psFree (stats);
     1559    }
     1560
    13041561    psFree(residual);
    13051562    psFree(polyValues);
    13061563
     1564
    13071565    return deviations;
    13081566}
     1567
     1568// we are supplied U, not Ut; w represents a diagonal matrix (also, we apply 1/w instead of w)
     1569psImage *p_pmSubSolve_wUt (psVector *w, psImage *U) {
     1570
     1571    psAssert (w->n == U->numCols, "w and U dimensions do not match");
     1572
     1573    // wUt has dimensions transposed relative to Ut.
     1574    psImage *wUt = psImageAlloc (U->numRows, U->numCols, PS_TYPE_F64);
     1575    psImageInit (wUt, 0.0);
     1576
     1577    for (int i = 0; i < wUt->numCols; i++) {
     1578        for (int j = 0; j < wUt->numRows; j++) {
     1579            if (!isfinite(w->data.F64[j])) continue;
     1580            if (w->data.F64[j] == 0.0) continue;
     1581            wUt->data.F64[j][i] = U->data.F64[i][j] / w->data.F64[j];
     1582        }
     1583    }
     1584    return wUt;
     1585}
     1586
     1587// XXX this is just standard matrix multiplication: use psMatrixMultiply?
     1588psImage *p_pmSubSolve_VwUt (psImage *V, psImage *wUt) {
     1589
     1590    psAssert (V->numCols == wUt->numRows, "matrix dimensions do not match");
     1591
     1592    psImage *Ainv = psImageAlloc (wUt->numCols, V->numRows, PS_TYPE_F64);
     1593
     1594    for (int i = 0; i < Ainv->numCols; i++) {
     1595        for (int j = 0; j < Ainv->numRows; j++) {
     1596            double sum = 0.0;
     1597            for (int k = 0; k < V->numCols; k++) {
     1598                sum += V->data.F64[j][k] * wUt->data.F64[k][i];
     1599            }
     1600            Ainv->data.F64[j][i] = sum;
     1601        }
     1602    }
     1603    return Ainv;
     1604}
     1605
     1606// we are supplied U, not Ut
     1607bool p_pmSubSolve_UtB (psVector **UtB, psImage *U, psVector *B) {
     1608
     1609    psAssert (U->numRows == B->n, "U and B dimensions do not match");
     1610
     1611    UtB[0] = psVectorRecycle (UtB[0], U->numCols, PS_TYPE_F64);
     1612
     1613    for (int i = 0; i < U->numCols; i++) {
     1614        double sum = 0.0;
     1615        for (int j = 0; j < U->numRows; j++) {
     1616            sum += B->data.F64[j] * U->data.F64[j][i];
     1617        }
     1618        UtB[0]->data.F64[i] = sum;
     1619    }
     1620    return true;
     1621}
     1622
     1623// w is diagonal
     1624bool p_pmSubSolve_wUtB (psVector **wUtB, psVector *w, psVector *UtB) {
     1625
     1626    psAssert (w->n == UtB->n, "w and UtB dimensions do not match");
     1627
     1628    // wUt has dimensions transposed relative to Ut.
     1629    wUtB[0] = psVectorRecycle (wUtB[0], w->n, PS_TYPE_F64);
     1630    psVectorInit (wUtB[0], 0.0);
     1631
     1632    for (int i = 0; i < w->n; i++) {
     1633        if (!isfinite(w->data.F64[i])) continue;
     1634        if (w->data.F64[i] == 0.0) continue;
     1635        wUtB[0]->data.F64[i] = UtB->data.F64[i] / w->data.F64[i];
     1636    }
     1637    return true;
     1638}
     1639
     1640// this is basically matrix * vector
     1641bool p_pmSubSolve_VwUtB (psVector **VwUtB, psImage *V, psVector *wUtB) {
     1642
     1643    psAssert (V->numCols == wUtB->n, "V and wUtB dimensions do not match");
     1644
     1645    VwUtB[0] = psVectorRecycle (*VwUtB, V->numRows, PS_TYPE_F64);
     1646
     1647    for (int j = 0; j < V->numRows; j++) {
     1648        double sum = 0.0;
     1649        for (int i = 0; i < V->numCols; i++) {
     1650            sum += V->data.F64[j][i] * wUtB->data.F64[i];
     1651        }
     1652        VwUtB[0]->data.F64[j] = sum;
     1653    }
     1654    return true;
     1655}
     1656
     1657// this is basically matrix * vector
     1658bool p_pmSubSolve_Ax (psVector **B, psImage *A, psVector *x) {
     1659
     1660    psAssert (A->numCols == x->n, "A and x dimensions do not match");
     1661
     1662    B[0] = psVectorRecycle (*B, A->numRows, PS_TYPE_F64);
     1663
     1664    for (int j = 0; j < A->numRows; j++) {
     1665        double sum = 0.0;
     1666        for (int i = 0; i < A->numCols; i++) {
     1667            sum += A->data.F64[j][i] * x->data.F64[i];
     1668        }
     1669        B[0]->data.F64[j] = sum;
     1670    }
     1671    return true;
     1672}
     1673
     1674// this is basically Vector * vector
     1675bool p_pmSubSolve_VdV (double *value, psVector *x, psVector *y) {
     1676
     1677    psAssert (x->n == y->n, "x and y dimensions do not match");
     1678
     1679    double sum = 0.0;
     1680    for (int i = 0; i < x->n; i++) {
     1681        sum += x->data.F64[i] * y->data.F64[i];
     1682    }
     1683    *value = sum;
     1684    return true;
     1685}
     1686
     1687bool p_pmSubSolve_y2 (double *y2, pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
     1688
     1689    int footprint = stamps->footprint; // Half-size of stamps
     1690
     1691    double sum = 0.0;
     1692    for (int i = 0; i < stamps->num; i++) {
     1693
     1694        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1695        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     1696
     1697        psKernel *weight = NULL;
     1698        psKernel *window = NULL;
     1699        psKernel *input = NULL;
     1700
     1701#ifdef USE_WEIGHT
     1702        weight = stamp->weight;
     1703#endif
     1704#ifdef USE_WINDOW
     1705        window = stamps->window;
     1706#endif
     1707
     1708        switch (kernels->mode) {
     1709            // MODE_1 : convolve image 1 to match image 2 (and vice versa)
     1710          case PM_SUBTRACTION_MODE_1:
     1711            input = stamp->image2;
     1712            break;
     1713          case PM_SUBTRACTION_MODE_2:
     1714            input = stamp->image1;
     1715            break;
     1716          default:
     1717            psAbort ("programming error");
     1718        }
     1719
     1720        for (int y = - footprint; y <= footprint; y++) {
     1721            for (int x = - footprint; x <= footprint; x++) {
     1722                double in = input->kernel[y][x];
     1723                double value = in*in;
     1724                if (weight) {
     1725                    float wtVal = weight->kernel[y][x];
     1726                    value *= wtVal;
     1727                }
     1728                if (window) {
     1729                    float  winVal = window->kernel[y][x];
     1730                    value *= winVal;
     1731                }
     1732                sum += value;
     1733            }
     1734        }
     1735    }
     1736    *y2 = sum;
     1737    return true;
     1738}
     1739
     1740double p_pmSubSolve_ChiSquare (pmSubtractionKernels *kernels, const pmSubtractionStampList *stamps) {
     1741
     1742    int footprint = stamps->footprint; // Half-size of stamps
     1743    int numKernels = kernels->num;      // Number of kernels
     1744
     1745    double sum = 0.0;
     1746
     1747    psKernel *residual = psKernelAlloc(-footprint, footprint, -footprint, footprint); // Residual image
     1748    psImageInit(residual->image, 0.0);
     1749
     1750    psImage *polyValues = NULL;         // Polynomial values
     1751
     1752    for (int i = 0; i < stamps->num; i++) {
     1753
     1754        pmSubtractionStamp *stamp = stamps->stamps->data[i];
     1755        if (stamp->status != PM_SUBTRACTION_STAMP_USED) continue;
     1756
     1757        psKernel *weight = NULL;
     1758        psKernel *window = NULL;
     1759        psKernel *target = NULL;
     1760        psKernel *source = NULL;
     1761
     1762        psArray *convolutions = NULL;
     1763
     1764#ifdef USE_WEIGHT
     1765        weight = stamp->weight;
     1766#endif
     1767#ifdef USE_WINDOW
     1768        window = stamps->window;
     1769#endif
     1770
     1771        switch (kernels->mode) {
     1772            // MODE_1 : convolve image 1 to match image 2 (and vice versa)
     1773          case PM_SUBTRACTION_MODE_1:
     1774            target = stamp->image2;
     1775            source = stamp->image1;
     1776            convolutions = stamp->convolutions1;
     1777            break;
     1778          case PM_SUBTRACTION_MODE_2:
     1779            target = stamp->image1;
     1780            source = stamp->image2;
     1781            convolutions = stamp->convolutions2;
     1782            break;
     1783          default:
     1784            psAbort ("programming error");
     1785        }
     1786
     1787        // Calculate coefficients of the kernel basis functions
     1788        polyValues = p_pmSubtractionPolynomial(polyValues, kernels->spatialOrder, stamp->xNorm, stamp->yNorm);
     1789        double norm = p_pmSubtractionSolutionNorm(kernels); // Normalisation
     1790        double background = p_pmSubtractionSolutionBackground(kernels, polyValues);// Difference in background
     1791
     1792        psImageInit(residual->image, 0.0);
     1793        for (int j = 0; j < numKernels; j++) {
     1794            psKernel *convolution = convolutions->data[j]; // Convolution
     1795            double coefficient = p_pmSubtractionSolutionCoeff(kernels, polyValues, j, false); // Coefficient
     1796            for (int y = - footprint; y <= footprint; y++) {
     1797                for (int x = - footprint; x <= footprint; x++) {
     1798                    residual->kernel[y][x] -= convolution->kernel[y][x] * coefficient;
     1799                }
     1800            }
     1801        }
     1802
     1803        for (int y = - footprint; y <= footprint; y++) {
     1804            for (int x = - footprint; x <= footprint; x++) {
     1805                double resid = target->kernel[y][x] - background - source->kernel[y][x] * norm + residual->kernel[y][x];
     1806                double value = PS_SQR(resid);
     1807                if (weight) {
     1808                    float wtVal = weight->kernel[y][x];
     1809                    value *= wtVal;
     1810                }
     1811                if (window) {
     1812                    float  winVal = window->kernel[y][x];
     1813                    value *= winVal;
     1814                }
     1815                sum += value;
     1816            }
     1817        }
     1818    }
     1819    psFree (polyValues);
     1820    psFree (residual);
     1821
     1822    return sum;
     1823}
     1824
     1825bool p_pmSubSolve_SetWeights (psVector *wApply, psVector *w, psVector *wMask) {
     1826
     1827    for (int i = 0; i < w->n; i++) {
     1828        wApply->data.F64[i] = wMask->data.U8[i] ? 0.0 : w->data.F64[i];
     1829    }
     1830    return true;
     1831}
     1832
     1833// we are supplied V and w; w represents a diagonal matrix (also, we apply 1/w instead of w)
     1834psImage *p_pmSubSolve_Xvar (psImage *V, psVector *w) {
     1835
     1836    psAssert (w->n == V->numCols, "w and U dimensions do not match");
     1837
     1838    psImage *Vn = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
     1839    psImageInit (Vn, 0.0);
     1840
     1841    // generate Vn = V * w^{-1}
     1842    for (int j = 0; j < Vn->numRows; j++) {
     1843        for (int i = 0; i < Vn->numCols; i++) {
     1844            if (!isfinite(w->data.F64[i])) continue;
     1845            if (w->data.F64[i] == 0.0) continue;
     1846            Vn->data.F64[j][i] = V->data.F64[j][i] / w->data.F64[i];
     1847        }
     1848    }
     1849
     1850    psImage *Xvar = psImageAlloc (V->numCols, V->numRows, PS_TYPE_F64);
     1851    psImageInit (Xvar, 0.0);
     1852
     1853    // generate Xvar = Vn * Vn^T
     1854    for (int j = 0; j < Vn->numRows; j++) {
     1855        for (int i = 0; i < Vn->numCols; i++) {
     1856            double sum = 0.0;
     1857            for (int k = 0; k < Vn->numCols; k++) {
     1858                sum += Vn->data.F64[k][i]*Vn->data.F64[k][j];
     1859            }
     1860            Xvar->data.F64[j][i] = sum;
     1861        }
     1862    }
     1863    return Xvar;
     1864}
     1865
     1866// I get confused by the index values between the image vs matrix usage:  In terms
     1867// of the elements of an image A(x,y) = A->data.F64[y][x] = A_x,y, a matrix
     1868// multiplication is: A_k,j * B_i,k = C_i,j
     1869
     1870
     1871bool psFitsWriteImageSimple (char *filename, psImage *image, psMetadata *header) {
     1872
     1873    psFits *fits = psFitsOpen(filename, "w");
     1874    psFitsWriteImage(fits, header, image, 0, NULL);
     1875    psFitsClose(fits);
     1876
     1877    return true;
     1878}
     1879
     1880bool psVectorWriteFile (char *filename, const psVector *vector) {
     1881
     1882    FILE *f = fopen (filename, "w");
     1883    int fd = fileno(f);
     1884    p_psVectorPrint (fd, vector, "unnamed");
     1885    fclose (f);
     1886
     1887    return true;
     1888}
     1889
     1890
     1891# if 0
     1892
     1893#ifdef TESTING
     1894        psFitsWriteImageSimple("A.fits", sumMatrix, NULL);
     1895        psVectorWriteFile ("B.dat", sumVector);
     1896#endif
     1897
     1898# define SVD_ANALYSIS 0
     1899# define COEFF_SIG 0.0
     1900# define SVD_TOL 0.0
     1901
     1902        // Use SVD to determine the kernel coeffs (and validate)
     1903        if (SVD_ANALYSIS) {
     1904
     1905            // We have sumVector and sumMatrix.  we are trying to solve the following equation:
     1906            // sumMatrix * x = sumVector.
     1907
     1908            // we can use any standard matrix inversion to solve this.  However, the basis
     1909            // functions in general have substantial correlation, so that the solution may be
     1910            // somewhat poorly determined or unstable.  If not numerically ill-conditioned, the
     1911            // system of equations may be statistically ill-conditioned.  Noise in the image
     1912            // will drive insignificant, but correlated, terms in the solution.  To avoid these
     1913            // problems, we can use SVD to identify numerically unconstrained values and to
     1914            // avoid statistically badly determined value.
     1915
     1916            // A = sumMatrix, B = sumVector
     1917            // SVD: A = U w V^T  -> A^{-1} = V (1/w) U^T
     1918            // x = V (1/w) (U^T B)
     1919            // \sigma_x = sqrt(diag(A^{-1}))
     1920            // solve for x and A^{-1} to get x & dx
     1921            // identify the elements of (1/w) that are nan (1/0.0) -> set to 0.0
     1922            // identify the elements of x that are insignificant (x / dx < 1.0? < 0.5?) -> set to 0.0
     1923
     1924            // If I use the SVD trick to re-condition the matrix, I need to break out the
     1925            // kernel and normalization terms from the background term.
     1926            // XXX is this true?  or was this due to an error in the analysis?
     1927
     1928            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     1929
     1930            // now pull out the kernel elements into their own square matrix
     1931            psImage  *kernelMatrix = psImageAlloc  (sumMatrix->numCols - 1, sumMatrix->numRows - 1, PS_TYPE_F64);
     1932            psVector *kernelVector = psVectorAlloc (sumMatrix->numCols - 1, PS_TYPE_F64);
     1933
     1934            for (int ix = 0, kx = 0; ix < sumMatrix->numCols; ix++) {
     1935                if (ix == bgIndex) continue;
     1936                for (int iy = 0, ky = 0; iy < sumMatrix->numRows; iy++) {
     1937                    if (iy == bgIndex) continue;
     1938                    kernelMatrix->data.F64[ky][kx] = sumMatrix->data.F64[iy][ix];
     1939                    ky++;
     1940                }
     1941                kernelVector->data.F64[kx] = sumVector->data.F64[ix];
     1942                kx++;
     1943            }
     1944
     1945            psImage *U = NULL;
     1946            psImage *V = NULL;
     1947            psVector *w = NULL;
     1948            if (!psMatrixSVD (&U, &w, &V, kernelMatrix)) {
     1949                psError(PS_ERR_UNKNOWN, false, "failed to perform SVD on sumMatrix\n");
     1950                return NULL;
     1951            }
     1952
     1953            // calculate A_inverse:
     1954            // Ainv = V * w * U^T
     1955            psImage *wUt  = p_pmSubSolve_wUt (w, U);
     1956            psImage *Ainv = p_pmSubSolve_VwUt (V, wUt);
     1957            psImage *Xvar = NULL;
     1958            psFree (wUt);
     1959
     1960# ifdef TESTING
     1961            // kernel terms:
     1962            for (int i = 0; i < w->n; i++) {
     1963                fprintf (stderr, "w: %f\n", w->data.F64[i]);
     1964            }
     1965# endif
     1966            // loop over w adding in more and more of the values until chisquare is no longer
     1967            // dropping significantly.
     1968            // XXX this does not seem to work very well: we seem to need all terms even for
     1969            // simple cases...
     1970
     1971            psVector *Xsvd = NULL;
     1972            {
     1973                psVector *Ax = NULL;
     1974                psVector *UtB = NULL;
     1975                psVector *wUtB = NULL;
     1976
     1977                psVector *wApply = psVectorAlloc(w->n, PS_TYPE_F64);
     1978                psVector *wMask = psVectorAlloc(w->n, PS_TYPE_U8);
     1979                psVectorInit (wMask, 1); // start by masking everything
     1980
     1981                double chiSquareLast = NAN;
     1982                int maxWeight = 0;
     1983
     1984                double Axx, Bx, y2;
     1985
     1986                // XXX this is an attempt to exclude insignificant modes.
     1987                // it was not successful with the ISIS kernel set: removing even
     1988                // the least significant mode leaves additional ringing / noise
     1989                // because the terms are so coupled.
     1990                for (int k = 0; false && (k < w->n); k++) {
     1991
     1992                    // unmask the k-th weight
     1993                    wMask->data.U8[k] = 0;
     1994                    p_pmSubSolve_SetWeights(wApply, w, wMask);
     1995
     1996                    // solve for x:
     1997                    // x = V * w * (U^T * B)
     1998                    p_pmSubSolve_UtB (&UtB, U, kernelVector);
     1999                    p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
     2000                    p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
     2001
     2002                    // chi-square for this system of equations:
     2003                    // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
     2004                    // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
     2005                    p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
     2006                    p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
     2007                    p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
     2008                    p_pmSubSolve_y2 (&y2, kernels, stamps);
     2009
     2010                    // apparently, this works (compare with the brute force value below
     2011                    double chiSquare = Axx - 2.0*Bx + y2;
     2012                    double deltaChi = (k == 0) ? chiSquare : chiSquareLast - chiSquare;
     2013                    chiSquareLast = chiSquare;
     2014
     2015                    // fprintf (stderr, "chi square = %f, delta: %f\n", chiSquare, deltaChi);
     2016                    if (k && !maxWeight && (deltaChi < 1.0)) {
     2017                        maxWeight = k;
     2018                    }
     2019                }
     2020
     2021                // keep all terms or we get extra ringing
     2022                maxWeight = w->n;
     2023                psVectorInit (wMask, 1);
     2024                for (int k = 0; k < maxWeight; k++) {
     2025                    wMask->data.U8[k] = 0;
     2026                }
     2027                p_pmSubSolve_SetWeights(wApply, w, wMask);
     2028
     2029                // solve for x:
     2030                // x = V * w * (U^T * B)
     2031                p_pmSubSolve_UtB (&UtB, U, kernelVector);
     2032                p_pmSubSolve_wUtB (&wUtB, wApply, UtB);
     2033                p_pmSubSolve_VwUtB (&Xsvd, V, wUtB);
     2034
     2035                // chi-square for this system of equations:
     2036                // chi-square = sum over terms of: (Ax - B)*x - b*x - y^2
     2037                // y^2 = \sum_stamps \sum_pixels input->kernel[y][x]^2
     2038                p_pmSubSolve_Ax (&Ax, kernelMatrix, Xsvd);
     2039                p_pmSubSolve_VdV (&Axx, Ax, Xsvd);
     2040                p_pmSubSolve_VdV (&Bx, kernelVector, Xsvd);
     2041                p_pmSubSolve_y2 (&y2, kernels, stamps);
     2042
     2043                // apparently, this works (compare with the brute force value below
     2044                double chiSquare = Axx - 2.0*Bx + y2;
     2045                psLogMsg ("psModules.imcombine", PS_LOG_INFO, "model kernel with %d terms; chi square = %f\n", maxWeight, chiSquare);
     2046
     2047                // re-calculate A^{-1} to get new variances:
     2048                // Ainv = V * w * U^T
     2049                // XXX since we keep all terms, this is identical to Ainv
     2050                psImage *wUt  = p_pmSubSolve_wUt (wApply, U);
     2051                Xvar = p_pmSubSolve_VwUt (V, wUt);
     2052                psFree (wUt);
     2053
     2054                psFree (Ax);
     2055                psFree (UtB);
     2056                psFree (wUtB);
     2057                psFree (wApply);
     2058                psFree (wMask);
     2059            }
     2060
     2061            // copy the kernel solutions to the full solution vector:
     2062            solution = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2063            solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2064
     2065            for (int ix = 0, kx = 0; ix < sumVector->n; ix++) {
     2066                if (ix == bgIndex) {
     2067                    solution->data.F64[ix] = 0;
     2068                    solutionErr->data.F64[ix] = 0.001;
     2069                    continue;
     2070                }
     2071                solutionErr->data.F64[ix] = sqrt(Ainv->data.F64[kx][kx]);
     2072                solution->data.F64[ix] = Xsvd->data.F64[kx];
     2073                kx++;
     2074            }
     2075
     2076            psFree (kernelMatrix);
     2077            psFree (kernelVector);
     2078
     2079            psFree (U);
     2080            psFree (V);
     2081            psFree (w);
     2082
     2083            psFree (Ainv);
     2084            psFree (Xsvd);
     2085        } else {
     2086            psVector *permutation = NULL;       // Permutation vector, required for LU decomposition
     2087            psImage *luMatrix = psMatrixLUDecomposition(NULL, &permutation, sumMatrix);
     2088            if (!luMatrix) {
     2089                psError(PS_ERR_UNKNOWN, true, "LU Decomposition of least-squares matrix failed.\n");
     2090                psFree(solution);
     2091                psFree(sumVector);
     2092                psFree(sumMatrix);
     2093                psFree(luMatrix);
     2094                psFree(permutation);
     2095                return NULL;
     2096            }
     2097
     2098            solution = psMatrixLUSolution(NULL, luMatrix, sumVector, permutation);
     2099            psFree(luMatrix);
     2100            psFree(permutation);
     2101            if (!solution) {
     2102                psError(PS_ERR_UNKNOWN, true, "Failed to solve the least-squares system.\n");
     2103                psFree(solution);
     2104                psFree(sumVector);
     2105                psFree(sumMatrix);
     2106                return NULL;
     2107            }
     2108
     2109            // XXX LUD does not provide A^{-1}?  fake the error for now
     2110            solutionErr = psVectorAlloc(sumVector->n, PS_TYPE_F64);
     2111            for (int ix = 0; ix < sumVector->n; ix++) {
     2112                solutionErr->data.F64[ix] = 0.1*solution->data.F64[ix];
     2113            }
     2114        }
     2115
     2116        if (!kernels->solution1) {
     2117            kernels->solution1 = psVectorAlloc (sumVector->n, PS_TYPE_F64);
     2118            psVectorInit (kernels->solution1, 0.0);
     2119        }
     2120
     2121        // only update the solutions that we chose to calculate:
     2122        if (mode & PM_SUBTRACTION_EQUATION_NORM) {
     2123            int normIndex = PM_SUBTRACTION_INDEX_NORM(kernels); // Index for normalisation
     2124            kernels->solution1->data.F64[normIndex] = solution->data.F64[normIndex];
     2125        }
     2126        if (mode & PM_SUBTRACTION_EQUATION_BG) {
     2127            int bgIndex = PM_SUBTRACTION_INDEX_BG(kernels); // Index in matrix for background
     2128            kernels->solution1->data.F64[bgIndex] = solution->data.F64[bgIndex];
     2129        }
     2130        if (mode & PM_SUBTRACTION_EQUATION_KERNELS) {
     2131            int numKernels = kernels->num;
     2132            int spatialOrder = kernels->spatialOrder;       // Order of spatial variation
     2133            int numPoly = PM_SUBTRACTION_POLYTERMS(spatialOrder); // Number of polynomial terms
     2134            for (int i = 0; i < numKernels * numPoly; i++) {
     2135                // XXX fprintf (stderr, "%f +/- %f (%f) -> ", solution->data.F64[i], solutionErr->data.F64[i], fabs(solution->data.F64[i]/solutionErr->data.F64[i]));
     2136                if (fabs(solution->data.F64[i] / solutionErr->data.F64[i]) < COEFF_SIG) {
     2137                    // XXX fprintf (stderr, "drop\n");
     2138                    kernels->solution1->data.F64[i] = 0.0;
     2139                } else {
     2140                    // XXX fprintf (stderr, "keep\n");
     2141                    kernels->solution1->data.F64[i] = solution->data.F64[i];
     2142                }
     2143            }
     2144        }
     2145        // double chiSquare = p_pmSubSolve_ChiSquare (kernels, stamps);
     2146        // fprintf (stderr, "chi square Brute = %f\n", chiSquare);
     2147
     2148        psFree(solution);
     2149        psFree(sumVector);
     2150        psFree(sumMatrix);
     2151# endif
     2152
     2153#ifdef TESTING
     2154              // XXX double-check for NAN in data:
     2155                for (int iy = 0; iy < stamp->matrix->numRows; iy++) {
     2156                    for (int ix = 0; ix < stamp->matrix->numCols; ix++) {
     2157                        if (!isfinite(stamp->matrix->data.F64[iy][ix])) {
     2158                            fprintf (stderr, "WARNING: NAN in matrix\n");
     2159                        }
     2160                    }
     2161                }
     2162                for (int ix = 0; ix < stamp->vector->n; ix++) {
     2163                    if (!isfinite(stamp->vector->data.F64[ix])) {
     2164                        fprintf (stderr, "WARNING: NAN in vector\n");
     2165                    }
     2166                }
     2167#endif
     2168
     2169#ifdef TESTING
     2170        for (int ix = 0; ix < sumVector->n; ix++) {
     2171            if (!isfinite(sumVector->data.F64[ix])) {
     2172                fprintf (stderr, "WARNING: NAN in vector\n");
     2173            }
     2174        }
     2175#endif
     2176
     2177#ifdef TESTING
     2178        for (int ix = 0; ix < sumVector->n; ix++) {
     2179            if (!isfinite(sumVector->data.F64[ix])) {
     2180                fprintf (stderr, "WARNING: NAN in vector\n");
     2181            }
     2182        }
     2183        {
     2184            psImage *inverse = psMatrixInvert(NULL, sumMatrix, NULL);
     2185            psFitsWriteImageSimple("matrixInv.fits", inverse, NULL);
     2186            psFree(inverse);
     2187        }
     2188        {
     2189            psImage *X = psMatrixInvert(NULL, sumMatrix, NULL);
     2190            psImage *Xt = psMatrixTranspose(NULL, X);
     2191            psImage *XtX = psMatrixMultiply(NULL, Xt, X);
     2192            psFitsWriteImageSimple("matrixErr.fits", XtX, NULL);
     2193            psFree(X);
     2194            psFree(Xt);
     2195            psFree(XtX);
     2196        }
     2197#endif
     2198
Note: See TracChangeset for help on using the changeset viewer.