/* * poly.c * * Polynomial functions. * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% * * This file part of: AstrOmatic software * * Copyright: (C) 1998-2012 IAP/CNRS/UPMC * * License: GNU General Public License * * AstrOmatic software is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * AstrOmatic software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with AstrOmatic software. * If not, see . * * Last modified: 20/11/2012 * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "poly.h" #ifdef HAVE_ATLAS #include ATLAS_LAPACK_H #endif #ifdef HAVE_LAPACKE #include LAPACKE_H //#define MATSTORAGE_PACKED 1 #endif #define QCALLOC(ptr, typ, nel) \ {if (!(ptr = (typ *)calloc((size_t)(nel),sizeof(typ)))) \ qerror("Not enough memory for ", \ #ptr " (" #nel " elements) !");;} #define QMALLOC(ptr, typ, nel) \ {if (!(ptr = (typ *)malloc((size_t)(nel)*sizeof(typ)))) \ qerror("Not enough memory for ", \ #ptr " (" #nel " elements) !");;} #define QMEMCPY(ptrin, ptrout, typ, nel) \ {if (ptrin) \ {if (!(ptrout = (typ *)malloc((size_t)(nel)*sizeof(typ)))) \ qerror("Not enough memory for ", \ #ptrout " (" #nel " elements) !"); \ memcpy(ptrout, ptrin, (size_t)(nel)*sizeof(typ));};;} /********************************* qerror ************************************/ /* I hope it will never be used! */ void qerror(char *msg1, char *msg2) { fprintf(stderr, "\n> %s%s\n\n",msg1,msg2); exit(-1); } /****** poly_init ************************************************************ PROTO polystruct *poly_init(int *group, int ndim, int *degree, int ngroup) PURPOSE Allocate and initialize a polynom structure. INPUT 1D array containing the group for each parameter, number of dimensions (parameters), 1D array with the polynomial degree for each group, number of groups. OUTPUT polystruct pointer. NOTES -. AUTHOR E. Bertin (IAP) VERSION 30/08/2011 ***/ polystruct *poly_init(int *group, int ndim, int *degree, int ngroup) { void qerror(char *msg1, char *msg2); polystruct *poly; char str[512]; int nd[POLY_MAXDIM]; int *groupt, d,g,n, num,den, dmax; QCALLOC(poly, polystruct, 1); if ((poly->ndim=ndim) > POLY_MAXDIM) { sprintf(str, "The dimensionality of the polynom (%d) exceeds the maximum\n" "allowed one (%d)", ndim, POLY_MAXDIM); qerror("*Error*: ", str); } if (ndim) QMALLOC(poly->group, int, poly->ndim); for (groupt=poly->group, d=ndim; d--;) *(groupt++) = *(group++)-1; poly->ngroup = ngroup; if (ngroup) { group = poly->group; /* Forget the original *group */ QMALLOC(poly->degree, int, poly->ngroup); /*-- Compute the number of context parameters for each group */ memset(nd, 0, ngroup*sizeof(int)); for (d=0; d=ngroup) qerror("*Error*: polynomial GROUP out of range", ""); nd[g]++; } } /* Compute the total number of coefficients */ poly->ncoeff = 1; for (g=0; gdegree[g]=*(degree++))>POLY_MAXDEGREE) { sprintf(str, "The degree of the polynom (%d) exceeds the maximum\n" "allowed one (%d)", poly->degree[g], POLY_MAXDEGREE); qerror("*Error*: ", str); } /*-- There are (n+d)!/(n!d!) coeffs per group = Prod_(i<=d)(n+i)/Prod_(i<=d)i */ n = nd[g]; d = dmax>n? n: dmax; for (num=den=1; d; num*=(n+dmax--), den*=d--); poly->ncoeff *= num/den; } QMALLOC(poly->basis, double, poly->ncoeff); QCALLOC(poly->coeff, double, poly->ncoeff); return poly; } /****** poly_end ************************************************************* PROTO void poly_end(polystruct *poly) PURPOSE Free a polynom structure and everything it contains. INPUT polystruct pointer. OUTPUT -. NOTES -. AUTHOR E. Bertin (IAP) VERSION 04/11/2008 ***/ void poly_end(polystruct *poly) { if (poly) { free(poly->coeff); free(poly->basis); free(poly->orthobasis); free(poly->degree); free(poly->group); free(poly->orthomat); free(poly->deorthomat); free(poly); } return; } /****** poly_copy ************************************************************* PROTO polystruct *poly_copy(polystruct *poly) PURPOSE Copy a polynom structure and everything it contains. INPUT polystruct pointer. OUTPUT -. NOTES -. AUTHOR E. Bertin (IAP) VERSION 04/11/2008 ***/ polystruct *poly_copy(polystruct *poly) { polystruct *newpoly; if (poly) { QMALLOC(newpoly, polystruct, 1); *newpoly = *poly; if (poly->ncoeff) { QMEMCPY(poly->coeff, newpoly->coeff, double, poly->ncoeff); QMEMCPY(poly->basis, newpoly->basis, double, poly->ncoeff); } if (poly->ndim) QMEMCPY(poly->group, newpoly->group, int, poly->ndim); if (poly->ngroup) QMEMCPY(poly->degree, newpoly->degree, int, poly->ngroup); if (poly->orthomat) { QMEMCPY(poly->orthomat, newpoly->orthomat, double, poly->ncoeff*poly->ncoeff); QMEMCPY(poly->deorthomat, newpoly->deorthomat, double, poly->ncoeff*poly->ncoeff); QMEMCPY(poly->orthobasis, newpoly->orthobasis, double, poly->ncoeff); } return newpoly; } else return NULL; } /****** poly_func ************************************************************ PROTO double poly_func(polystruct *poly, double *pos) PURPOSE Evaluate a multidimensional polynomial. INPUT polystruct pointer, pointer to the 1D array of input vector data. OUTPUT Polynom value. NOTES Values of the basis functions are updated in poly->basis. AUTHOR E. Bertin (IAP) VERSION 03/03/2004 ***/ double poly_func(polystruct *poly, double *pos) { double xpol[POLY_MAXDIM+1]; double *post, *xpolt, *basis, *coeff, xval; long double val; int expo[POLY_MAXDIM+1], gexpo[POLY_MAXDIM+1]; int *expot, *degree,*degreet, *group,*groupt, *gexpot, d,g,t, ndim; /* Prepare the vectors and counters */ ndim = poly->ndim; basis = poly->basis; coeff = poly->coeff; group = poly->group; degree = poly->degree; if (ndim) { for (xpolt=xpol, expot=expo, post=pos, d=ndim; --d;) { *(++xpolt) = 1.0; *(++expot) = 0; } for (gexpot=gexpo, degreet=degree, g=poly->ngroup; g--;) *(gexpot++) = *(degreet++); if (gexpo[*group]) gexpo[*group]--; } /* The constant term is handled separately */ val = *(coeff++); *(basis++) = 1.0; *expo = 1; *xpol = *pos; /* Compute the rest of the polynom */ for (t=poly->ncoeff; --t; ) { /*-- xpol[0] contains the current product of the x^n's */ val += (*(basis++)=*xpol)**(coeff++); /*-- A complex recursion between terms of the polynom speeds up computations */ /*-- Not too good for roundoff errors (prefer Horner's), but much easier for */ /*-- multivariate polynomials: this is why we use a long double accumulator */ post = pos; groupt = group; expot = expo; xpolt = xpol; for (d=0; dbasis. AUTHOR E. Bertin (IAP) VERSION 29/01/2013 ***/ double poly_cfunc(polystruct *poly, double *pos) { double pol[POLY_MAXDIM*(POLY_MAXDEGREE+1)], *polt, *post, *basis, *coeff, xval; long double val; int expo[POLY_MAXDIM+1], gexpo[POLY_MAXDIM+1]; int *expot, *degree,*degreet, *group,*groupt, *gexpot, d,d2,g,t, ndim; /* Prepare the vectors and counters */ ndim = poly->ndim; basis = poly->basis; coeff = poly->coeff; group = poly->group; degree = poly->degree; if (ndim) { for (groupt=group, expot=expo, post=pos, d=0; d 0; polt++) *polt = 2.0*xval**(polt-1) - *(polt-2); } for (gexpot=gexpo, degreet=degree, g=poly->ngroup; g--;) *(gexpot++) = *(degreet++); if (gexpo[*group]) gexpo[*group]--; } /* The constant term is handled separately */ val = *(coeff++); *(basis++) = 1.0; *expo = 1; /* Compute the rest of the polynom */ for (t=poly->ncoeff; --t; ) { polt = pol; expot = expo; /*-- xval contains the current product of the polynomials */ xval = 1.0; for (d=ndim; d--; polt += POLY_MAXDEGREE+1) xval *= polt[*(expot++)]; val += (*(basis++)=xval)**(coeff++); /*-- A complex recursion between terms of the polynom speeds up computations */ /*-- Not too good for roundoff errors (prefer Horner's), but much easier for */ /*-- multivariate polynomials: this is why we use a long double accumulator */ expot = expo; groupt = group; for (d=0; dncoeff; ndim = poly->ndim; matsize = ncoeff*ncoeff; basis = poly->basis; extbasist = extbasis; QCALLOC(alpha, double, matsize); QCALLOC(beta, double, ncoeff); /* Subtract an average offset to maintain precision (droped for now ) */ /* if (x) { for (d=0; dPOLY_TINY) /*-- Simple Tikhonov regularization */ for (i=0; icoeff; for (j=ncoeff; j--;) *(coeff++) = *(betat++); /* poly_addcste(poly, offset); */ free(beta); return info; } /****** poly_addcste ********************************************************* PROTO void poly_addcste(polystruct *poly, double *cste) PURPOSE Modify matrix coefficients to mimick the effect of adding a cst to the input of a polynomial. INPUT Pointer to the polynomial structure, Pointer to the vector of cst. OUTPUT -. NOTES Requires quadruple-precision. **For the time beeing, this function returns completely wrong results!!** AUTHOR E. Bertin (IAP) VERSION 05/10/2010 ***/ void poly_addcste(polystruct *poly, double *cste) { long double *acoeff; double *coeff,*mcoeff,*mcoefft, val; int *mpowers,*powers,*powerst,*powerst2, i,j,n,p, denum, flag, maxdegree, ncoeff, ndim; ncoeff = poly->ncoeff; ndim = poly->ndim; maxdegree = 0; for (j=0; jngroup; j++) if (maxdegree < poly->degree[j]) maxdegree = poly->degree[j]; maxdegree++; /* Actually we need maxdegree+1 terms */ QCALLOC(acoeff, long double, ncoeff); QCALLOC(mcoeff, double, ndim*maxdegree); QCALLOC(mpowers, int, ndim); mcoefft = mcoeff; /* To avoid gcc -Wall warnings */ powerst = powers = poly_powers(poly); coeff = poly->coeff; for (i=0; indim; group = poly->group; degree = poly->degree; QMALLOC(powers, int, ndim*poly->ncoeff); if (ndim) { for (expot=expo, d=ndim; --d;) *(++expot) = 0; for (gexpot=gexpo, degreet=degree, g=poly->ngroup; g--;) *(gexpot++) = *(degreet++); if (gexpo[*group]) gexpo[*group]--; } /* The constant term is handled separately */ powerst = powers; for (d=0; dncoeff; --t; ) { for (d=0; dndim; ncoeff = poly->ncoeff; basis = poly->basis; coeff = poly->coeff; /* Allocate memory for orthonormalization matrix and vector */ QCALLOC(poly->deorthomat, double, ncoeff*ncoeff); QMALLOC(poly->orthobasis, double, poly->ncoeff); QMALLOC(rdiag, double, ncoeff); /* Do a QR decomposition of input vector set */ /* Vectors are stored as rows to speed up the Householder transformation */ n = ncoeff; m = ndata; invec = data; for (c=0; c POLY_TINY) { if (*invect0 < 0.0) scale = -scale; invect = invect0; for (i=ndmc; i--;) *(invect++) /= scale; *invect0 += 1.0; invect02 = invect0 + ndata; for (j=ncoeff-c; --j; invect02+=ndata) { s = 0.0; invect = invect0; invect2 = invect02; for (i=ndmc; i--;) s += *(invect++)**(invect2++); s /= -*invect0; invect = invect0; invect2 = invect02; for (i=ndmc; i--;) *(invect2++) += s**(invect++); } } rdiag[c] = -scale; } /* Convert to deorthonormalization matrix */ deortho = poly->deorthomat; for (j=0; jdeorthomat, poly->orthomat, double, ncoeff*ncoeff); #if defined(HAVE_LAPACKE) LAPACKE_dtrtri(LAPACK_ROW_MAJOR, 'L', 'N', ncoeff,poly->orthomat,ncoeff); #elif defined(HAVE_ATLAS) clapack_dtrtri(CblasRowMajor, CblasLower, CblasNonUnit, ncoeff, poly->orthomat, ncoeff); #else qerror("*Internal Error*: no routine available", " for triangular inverse"); #endif /* Transpose orthonormalization matrix to speed up later use */ deortho = poly->deorthomat; for (j=0; j"). INPUT polystruct pointer, pointer to the input vector, pointer to the output vector. OUTPUT Pointer to poly->orthobasis, or poly->basis if no ortho. matrix exists. NOTES The poly->basis vector must have been updated with poly_func() first. AUTHOR E. Bertin (IAP) VERSION 04/11/2008 ***/ double *poly_ortho(polystruct *poly, double *datain, double *dataout) { double *omat,*basis,*obasis, dval; int i,j, ncoeff; if (!poly->orthomat) return datain; ncoeff = poly->ncoeff; /* Compute matrix product */ omat = poly->orthomat; obasis = dataout; for (j=ncoeff; j--;) { basis = datain; dval = 0.0; for (i=ncoeff; i--;) dval += *(omat++)**(basis++); *(obasis++) = dval; } return dataout; } /****** poly_deortho ********************************************************** PROTO void poly_deortho(polystruct *poly, double *datain, double *dataout) PURPOSE Apply deorthonormalization to the poly basis component vector("basis. NOTES -. AUTHOR E. Bertin (IAP) VERSION 04/11/2008 ***/ double *poly_deortho(polystruct *poly, double *datain, double *dataout) { double *omat,*basis,*obasis, dval; int i,j, ncoeff; if (!poly->deorthomat) return datain; ncoeff = poly->ncoeff; /* Compute matrix product */ omat = poly->deorthomat; basis = dataout; for (j=ncoeff; j--;) { obasis = datain; dval = 0.0; for (i=ncoeff; i--;) dval += *(omat++)**(obasis++); *(basis++) = dval; } return dataout; }