public software.sextractor

[/] [trunk/] [src/] [wcs/] [poly.c] - Blame information for rev 293

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 233 bertin
/*
2
*                               poly.c
3 2 bertin
*
4 233 bertin
* Manage polynomials.
5 2 bertin
*
6 233 bertin
*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7 2 bertin
*
8 233 bertin
*       This file part of:      AstrOmatic WCS library
9 2 bertin
*
10 264 bertin
*       Copyright:              (C) 1998-2011 Emmanuel Bertin -- IAP/CNRS/UPMC
11 2 bertin
*
12 233 bertin
*       License:                GNU General Public License
13
*
14
*       AstrOmatic software is free software: you can redistribute it and/or
15
*       modify it under the terms of the GNU General Public License as
16
*       published by the Free Software Foundation, either version 3 of the
17
*       License, or (at your option) any later version.
18
*       AstrOmatic software is distributed in the hope that it will be useful,
19
*       but WITHOUT ANY WARRANTY; without even the implied warranty of
20
*       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
*       GNU General Public License for more details.
22
*       You should have received a copy of the GNU General Public License
23
*       along with AstrOmatic software.
24
*       If not, see <http://www.gnu.org/licenses/>.
25
*
26 293 bertin
*       Last modified:          20/12/2011
27 233 bertin
*
28
*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
29 2 bertin
 
30
#ifdef HAVE_CONFIG_H
31
#include        "config.h"
32
#endif
33
 
34
#include        <math.h>
35
#include        <stdio.h>
36
#include        <stdlib.h>
37
#include        <string.h>
38
 
39 233 bertin
#ifdef HAVE_ATLAS
40 293 bertin
#include ATLAS_LAPACK_H
41 233 bertin
#endif
42 2 bertin
 
43 293 bertin
#ifdef HAVE_LAPACKE
44
#include LAPACKE_H
45
#endif
46
 
47
#include        "poly.h"
48
 
49 2 bertin
#define QCALLOC(ptr, typ, nel) \
50
                {if (!(ptr = (typ *)calloc((size_t)(nel),sizeof(typ)))) \
51
                  qerror("Not enough memory for ", \
52
                        #ptr " (" #nel " elements) !");;}
53
 
54
#define QMALLOC(ptr, typ, nel) \
55
                {if (!(ptr = (typ *)malloc((size_t)(nel)*sizeof(typ)))) \
56
                  qerror("Not enough memory for ", \
57
                        #ptr " (" #nel " elements) !");;}
58
 
59
/********************************* qerror ************************************/
60
/*
61
I hope it will never be used!
62
*/
63
void    qerror(char *msg1, char *msg2)
64
  {
65
  fprintf(stderr, "\n> %s%s\n\n",msg1,msg2);
66
  exit(-1);
67
  }
68
 
69
 
70
/****** poly_init ************************************************************
71
PROTO   polystruct *poly_init(int *group, int ndim, int *degree, int ngroup)
72
PURPOSE Allocate and initialize a polynom structure.
73
INPUT   1D array containing the group for each parameter,
74
        number of dimensions (parameters),
75
        1D array with the polynomial degree for each group,
76
        number of groups.
77
OUTPUT  polystruct pointer.
78
NOTES   -.
79
AUTHOR  E. Bertin (IAP)
80 264 bertin
VERSION 30/08/2011
81 2 bertin
 ***/
82
polystruct      *poly_init(int *group, int ndim, int *degree, int ngroup)
83
  {
84
   void qerror(char *msg1, char *msg2);
85
   polystruct   *poly;
86
   char         str[512];
87
   int          nd[POLY_MAXDIM];
88
   int          *groupt,
89 264 bertin
                d,g,n, num,den, dmax;
90 2 bertin
 
91
  QCALLOC(poly, polystruct, 1);
92
  if ((poly->ndim=ndim) > POLY_MAXDIM)
93
    {
94
    sprintf(str, "The dimensionality of the polynom (%d) exceeds the maximum\n"
95
                "allowed one (%d)", ndim, POLY_MAXDIM);
96
    qerror("*Error*: ", str);
97
    }
98
 
99
  if (ndim)
100
    QMALLOC(poly->group, int, poly->ndim);
101
    for (groupt=poly->group, d=ndim; d--;)
102
      *(groupt++) = *(group++)-1;
103
 
104
  poly->ngroup = ngroup;
105
  if (ngroup)
106
    {
107
    group = poly->group;        /* Forget the original *group */
108
 
109
    QMALLOC(poly->degree, int, poly->ngroup);
110
 
111
/*-- Compute the number of context parameters for each group */
112
    memset(nd, 0, ngroup*sizeof(int));
113
    for (d=0; d<ndim; d++)
114
      {
115
      if ((g=group[d])>=ngroup)
116
        qerror("*Error*: polynomial GROUP out of range", "");
117
      nd[g]++;
118
      }
119
    }
120
 
121
/* Compute the total number of coefficients */
122
  poly->ncoeff = 1;
123
  for (g=0; g<ngroup; g++)
124
    {
125 264 bertin
    if ((dmax=poly->degree[g]=*(degree++))>POLY_MAXDEGREE)
126 2 bertin
      {
127
      sprintf(str, "The degree of the polynom (%d) exceeds the maximum\n"
128
                "allowed one (%d)", poly->degree[g], POLY_MAXDEGREE);
129
      qerror("*Error*: ", str);
130
      }
131
 
132 264 bertin
/*-- There are (n+d)!/(n!d!) coeffs per group = Prod_(i<=d)(n+i)/Prod_(i<=d)i */
133
    n = nd[g];
134
    d = dmax>n? n: dmax;
135
    for (num=den=1; d; num*=(n+dmax--), den*=d--);
136 2 bertin
    poly->ncoeff *= num/den;
137
    }
138
 
139
  QMALLOC(poly->basis, double, poly->ncoeff);
140
  QCALLOC(poly->coeff, double, poly->ncoeff);
141
 
142
  return poly;
143
  }
144
 
145
 
146
/****** poly_end *************************************************************
147
PROTO   void poly_end(polystruct *poly)
148
PURPOSE Free a polynom structure and everything it contains.
149
INPUT   polystruct pointer.
150
OUTPUT  -.
151
NOTES   -.
152
AUTHOR  E. Bertin (IAP, Leiden observatory & ESO)
153
VERSION 09/04/2000
154
 ***/
155
void    poly_end(polystruct *poly)
156
  {
157
  if (poly)
158
    {
159
    free(poly->coeff);
160
    free(poly->basis);
161
    free(poly->degree);
162
    free(poly->group);
163
    free(poly);
164
    }
165
  }
166
 
167
 
168
/****** poly_func ************************************************************
169
PROTO   double poly_func(polystruct *poly, double *pos)
170
PURPOSE Evaluate a multidimensional polynom.
171
INPUT   polystruct pointer,
172
        pointer to the 1D array of input vector data.
173
OUTPUT  Polynom value.
174
NOTES   Values of the basis functions are updated in poly->basis.
175
AUTHOR  E. Bertin (IAP)
176
VERSION 03/03/2004
177
 ***/
178
double  poly_func(polystruct *poly, double *pos)
179
  {
180
   double       xpol[POLY_MAXDIM+1];
181
   double       *post, *xpolt, *basis, *coeff, xval;
182
   long double  val;
183
   int          expo[POLY_MAXDIM+1], gexpo[POLY_MAXDIM+1];
184
   int          *expot, *degree,*degreet, *group,*groupt, *gexpot,
185
                        d,g,t, ndim;
186
 
187
/* Prepare the vectors and counters */
188
  ndim = poly->ndim;
189
  basis = poly->basis;
190
  coeff = poly->coeff;
191
  group = poly->group;
192
  degree = poly->degree;
193
  if (ndim)
194
    {
195
    for (xpolt=xpol, expot=expo, post=pos, d=ndim; --d;)
196
      {
197
      *(++xpolt) = 1.0;
198
      *(++expot) = 0;
199
      }
200
    for (gexpot=gexpo, degreet=degree, g=poly->ngroup; g--;)
201
      *(gexpot++) = *(degreet++);
202
    if (gexpo[*group])
203
      gexpo[*group]--;
204
    }
205
 
206
/* The constant term is handled separately */
207
  val = *(coeff++);
208
  *(basis++) = 1.0;
209
  *expo = 1;
210
  *xpol = *pos;
211
 
212
/* Compute the rest of the polynom */
213
  for (t=poly->ncoeff; --t; )
214
    {
215
/*-- xpol[0] contains the current product of the x^n's */
216
    val += (*(basis++)=*xpol)**(coeff++);
217
/*-- A complex recursion between terms of the polynom speeds up computations */
218
/*-- Not too good for roundoff errors (prefer Horner's), but much easier for */
219
/*-- multivariate polynomials: this is why we use a long double accumulator */
220
    post = pos;
221
    groupt = group;
222
    expot = expo;
223
    xpolt = xpol;
224
    for (d=0; d<ndim; d++, groupt++)
225
      if (gexpo[*groupt]--)
226
        {
227
        ++*(expot++);
228
        xval = (*(xpolt--) *= *post);
229
        while (d--)
230
          *(xpolt--) = xval;
231
        break;
232
        }
233
      else
234
        {
235
        gexpo[*groupt] = *expot;
236
        *(expot++) = 0;
237
        *(xpolt++) = 1.0;
238
        post++;
239
        }
240
    }
241
 
242
  return (double)val;
243
  }
244
 
245
 
246
/****** poly_fit *************************************************************
247
PROTO   double poly_fit(polystruct *poly, double *x, double *y, double *w,
248
        int ndata, double *extbasis)
249
PURPOSE Least-Square fit of a multidimensional polynom to weighted data.
250
INPUT   polystruct pointer,
251
        pointer to the (pseudo)2D array of inputs to basis functions,
252
        pointer to the 1D array of data values,
253
        pointer to the 1D array of data weights,
254
        number of data points,
255
        pointer to a (pseudo)2D array of computed basis function values.
256
OUTPUT  Chi2 of the fit.
257
NOTES   If different from NULL, extbasis can be provided to store the
258
        values of the basis functions. If x==NULL and extbasis!=NULL, the
259
        precomputed basis functions stored in extbasis are used (which saves
260
        CPU). If w is NULL, all points are given identical weight.
261
AUTHOR  E. Bertin (IAP, Leiden observatory & ESO)
262 7 bertin
VERSION 08/03/2005
263 2 bertin
 ***/
264
void    poly_fit(polystruct *poly, double *x, double *y, double *w, int ndata,
265
                double *extbasis)
266
  {
267
   void qerror(char *msg1, char *msg2);
268 7 bertin
   double       /*offset[POLY_MAXDIM],*/x2[POLY_MAXDIM],
269 2 bertin
                *alpha,*alphat, *beta,*betat, *basis,*basis1,*basis2, *coeff,
270
                *extbasist,*xt,
271
                val,wval,yval;
272
   int          ncoeff, ndim, matsize,
273
                d,i,j,n;
274
 
275
  if (!x && !extbasis)
276
    qerror("*Internal Error*: One of x or extbasis should be "
277
        "different from NULL\nin ", "poly_func()");
278
  ncoeff = poly->ncoeff;
279
  ndim = poly->ndim;
280
  matsize = ncoeff*ncoeff;
281
  basis = poly->basis;
282
  extbasist = extbasis;
283
  QCALLOC(alpha, double, matsize);
284
  QCALLOC(beta, double, ncoeff);
285
 
286 7 bertin
/* Subtract an average offset to maintain precision (droped for now ) */
287
/*
288 2 bertin
  if (x)
289
    {
290
    for (d=0; d<ndim; d++)
291
      offset[d] = 0.0;
292
    xt = x;
293
    for (n=ndata; n--;)
294
      for (d=0; d<ndim; d++)
295
        offset[d] += *(xt++);
296
    for (d=0; d<ndim; d++)
297
      offset[d] /= (double)ndata;
298
    }
299 7 bertin
*/
300 2 bertin
/* Build the covariance matrix */
301 7 bertin
  xt = x;
302 2 bertin
  for (n=ndata; n--;)
303
    {
304
    if (x)
305
      {
306
/*---- If x!=NULL, compute the basis functions */
307 7 bertin
      for (d=0; d<ndim; d++)
308
        x2[d] = *(xt++)/* - offset[d]*/;
309
      poly_func(poly, x2);
310 2 bertin
/*---- If, in addition, extbasis is provided, then fill it */
311
      if (extbasis)
312
        for (basis1=basis,j=ncoeff; j--;)
313
          *(extbasist++) = *(basis1++);
314
      }
315
    else
316
/*---- If x==NULL, then rely on pre-computed basis functions */
317
      for (basis1=basis,j=ncoeff; j--;)
318
        *(basis1++) = *(extbasist++);
319
 
320
    basis1 = basis;
321
    wval = w? *(w++) : 1.0;
322
    yval = *(y++);
323
    betat = beta;
324
    alphat = alpha;
325
    for (j=ncoeff; j--;)
326
      {
327
      val = *(basis1++)*wval;
328
      *(betat++) += val*yval;
329
      for (basis2=basis,i=ncoeff; i--;)
330
        *(alphat++) += val**(basis2++);
331
      }
332
    }
333
 
334
/* Solve the system */
335
  poly_solve(alpha,beta,ncoeff);
336
 
337
  free(alpha);
338
 
339
/* Now fill the coeff array with the result of the fit */
340
  betat = beta;
341
  coeff = poly->coeff;
342
  for (j=ncoeff; j--;)
343
    *(coeff++) = *(betat++);
344 7 bertin
/*
345 2 bertin
  poly_addcste(poly, offset);
346 7 bertin
*/
347 2 bertin
  free(beta);
348
 
349
  return;
350
  }
351
 
352
 
353
/****** poly_addcste *********************************************************
354
PROTO   void poly_addcste(polystruct *poly, double *cste)
355
PURPOSE Modify matrix coefficients to mimick the effect of adding a cst to
356
        the input of a polynomial.
357
INPUT   Pointer to the polynomial structure,
358
        Pointer to the vector of cst.
359
OUTPUT  -.
360 7 bertin
NOTES   Requires quadruple-precision. **For the time beeing, this function
361
        returns completely wrong results!!**
362 2 bertin
AUTHOR  E. Bertin (IAP)
363 233 bertin
VERSION 05/10/2010
364 2 bertin
 ***/
365
void    poly_addcste(polystruct *poly, double *cste)
366
  {
367
   long double  *acoeff;
368
   double       *coeff,*mcoeff,*mcoefft,
369
                val;
370
   int          *mpowers,*powers,*powerst,*powerst2,
371
                i,j,n,p, denum, flag, maxdegree, ncoeff, ndim;
372
 
373
  ncoeff = poly->ncoeff;
374
  ndim = poly->ndim;
375
  maxdegree = 0;
376
  for (j=0; j<poly->ngroup; j++)
377
    if (maxdegree < poly->degree[j])
378
      maxdegree = poly->degree[j];
379
  maxdegree++;          /* Actually we need maxdegree+1 terms */
380
  QCALLOC(acoeff, long double, ncoeff);
381
  QCALLOC(mcoeff, double, ndim*maxdegree);
382
  QCALLOC(mpowers, int, ndim);
383
  mcoefft = mcoeff;             /* To avoid gcc -Wall warnings */
384
  powerst = powers = poly_powers(poly);
385
  coeff = poly->coeff;
386
  for (i=0; i<ncoeff; i++)
387
    {
388
    for (j=0; j<ndim; j++)
389
      {
390
      mpowers[j] = n = *(powerst++);
391
      mcoefft = mcoeff+j*maxdegree+n;
392
      denum = 1;
393
      val = 1.0;
394
      for (p=n+1; p--;)
395
        {
396
        *(mcoefft--) = val;
397
        val *= (cste[j]*(n--))/(denum++);       /* This is C_n^p X^(n-p) */
398
        }
399
      }
400
/*-- Update all valid coefficients */
401
    powerst2 = powers;
402
    for (p=0; p<ncoeff; p++)
403
      {
404
/*---- Check that this combination of powers is included in the series above */
405
      flag = 0;
406
      for (j=0; j<ndim; j++)
407
        if (mpowers[j] < powerst2[j])
408
          {
409
          flag = 1;
410
          powerst2 += ndim;
411
          break;
412
          }
413
      if (flag == 1)
414
        continue;
415
      val = 1.0;
416
      mcoefft = mcoeff;
417
      for (j=ndim; j--; mcoefft += maxdegree)
418
        val *= mcoefft[*(powerst2++)];
419
      acoeff[i] += val*coeff[p];
420
      }
421
    }
422
 
423
/* Add the new coefficients to the previous ones */
424
 
425
  for (i=0; i<ncoeff; i++)
426
    coeff[i] = (double)acoeff[i];
427
 
428
  free(acoeff);
429
  free(mcoeff);
430
  free(mpowers);
431
  free(powers);
432
 
433
  return;
434
  }
435
 
436
/****** poly_solve ************************************************************
437
PROTO   void poly_solve(double *a, double *b, int n)
438 233 bertin
PURPOSE Solve a system of linear equations, using Cholesky decomposition.
439 2 bertin
INPUT   Pointer to the (pseudo 2D) matrix of coefficients,
440
        pointer to the 1D column vector,
441
        matrix size.
442
OUTPUT  -.
443
NOTES   -.
444
AUTHOR  E. Bertin (IAP, Leiden observatory & ESO)
445 293 bertin
VERSION 20/12/2011
446 2 bertin
 ***/
447
void    poly_solve(double *a, double *b, int n)
448
  {
449 293 bertin
#if defined(HAVE_LAPACKE)
450
  LAPACKE_dposv(LAPACK_COL_MAJOR, 'L', n, 1, a, n, b, n);
451
#elif defined(HAVE_ATLAS)
452 233 bertin
  clapack_dposv(CblasRowMajor, CblasUpper, n, 1, a, n, b, n);
453
#else
454 293 bertin
  cholsolve(a,b,n);
455 233 bertin
#endif
456 2 bertin
 
457
  return;
458
  }
459
 
460 233 bertin
 
461 2 bertin
/****** cholsolve *************************************************************
462 293 bertin
PROTO   int cholsolve(double *a, double *b, int n)
463 233 bertin
PURPOSE Solve a system of linear equations, using Cholesky decomposition.
464
INPUT   Pointer to the (pseudo 2D) matrix of coefficients,
465
        pointer to the 1D column vector,
466
        matrix size.
467
OUTPUT  -1 if the matrix is not positive-definite, 0 otherwise.
468
NOTES   Based on algorithm described in Numerical Recipes, 2nd ed. (Chap 2.9).
469
        The matrix of coefficients must be symmetric and positive definite.
470
AUTHOR  E. Bertin (IAP)
471
VERSION 10/10/2010
472 2 bertin
 ***/
473
int     cholsolve(double *a, double *b, int n)
474
  {
475
   double       *p, *x, sum;
476
   int          i,j,k;
477
 
478
/* Allocate memory to store the diagonal elements */
479
  QMALLOC(p, double, n);
480
 
481
/* Cholesky decomposition */
482
  for (i=0; i<n; i++)
483
    for (j=i; j<n; j++)
484
      {
485 233 bertin
      sum = a[i*n+j];
486
      for (k=i; k--;)
487 2 bertin
        sum -= a[i*n+k]*a[j*n+k];
488
      if (i==j)
489
        {
490
        if (sum <= 0.0)
491
          {
492
          free(p);
493
          return -1;
494
          }
495
        p[i] = sqrt(sum);
496
        }
497
      else
498
        a[j*n+i] = sum/p[i];
499
      }
500
 
501
/* Solve the system */
502
  x = b;                /* Just to save memory:  the solution replaces b */
503
  for (i=0; i<n; i++)
504
    {
505 233 bertin
    for (sum=b[i],k=i; k--;)
506 2 bertin
      sum -= a[i*n+k]*x[k];
507
    x[i] = sum/p[i];
508
    }
509
 
510 233 bertin
  for (i=n; i--;)
511 2 bertin
    {
512 233 bertin
    sum = x[i];
513
    for (k=i; ++k<n;)
514 2 bertin
      sum -= a[k*n+i]*x[k];
515
    x[i] = sum/p[i];
516
    }
517
 
518
  free(p);
519
 
520
  return 0;
521
  }
522
 
523
 
524
/****** poly_powers ***********************************************************
525
PROTO   int *poly_powers(polystruct *poly)
526
PURPOSE Return an array of powers of polynom terms
527
INPUT   polystruct pointer,
528
OUTPUT  Pointer to an array of polynom powers (int *), (ncoeff*ndim numbers).
529
NOTES   The returned pointer is mallocated.
530
AUTHOR  E. Bertin (IAP)
531
VERSION 23/10/2003
532
 ***/
533
int     *poly_powers(polystruct *poly)
534
  {
535
   int          expo[POLY_MAXDIM+1], gexpo[POLY_MAXDIM+1];
536
   int          *expot, *degree,*degreet, *group,*groupt, *gexpot,
537
                *powers, *powerst,
538
                d,g,t, ndim;
539
 
540
/* Prepare the vectors and counters */
541
  ndim = poly->ndim;
542
  group = poly->group;
543
  degree = poly->degree;
544
  QMALLOC(powers, int, ndim*poly->ncoeff);
545
  if (ndim)
546
    {
547
    for (expot=expo, d=ndim; --d;)
548
      *(++expot) = 0;
549
    for (gexpot=gexpo, degreet=degree, g=poly->ngroup; g--;)
550
      *(gexpot++) = *(degreet++);
551
    if (gexpo[*group])
552
      gexpo[*group]--;
553
    }
554
 
555
/* The constant term is handled separately */
556
  powerst = powers;
557
  for (d=0; d<ndim; d++)
558
    *(powerst++) = 0;
559
  *expo = 1;
560
 
561
/* Compute the rest of the polynom */
562
  for (t=poly->ncoeff; --t; )
563
    {
564
    for (d=0; d<ndim; d++)
565
      *(powerst++) = expo[d];
566
/*-- A complex recursion between terms of the polynom speeds up computations */
567
    groupt = group;
568
    expot = expo;
569
    for (d=0; d<ndim; d++, groupt++)
570
      if (gexpo[*groupt]--)
571
        {
572
        ++*(expot++);
573
        break;
574
        }
575
      else
576
        {
577
        gexpo[*groupt] = *expot;
578
        *(expot++) = 0;
579
        }
580
    }
581
 
582
  return powers;
583
  }
584