IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Oct 11, 2021, 11:33:33 AM (5 years ago)
Author:
eugene
Message:

add chebyshev polynomials to 2D fitting; add chebyshev support functions; use chebyshev polynomials for inverse transformation

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/ipp-dev-20210817/psLib/src/math/psPolynomial.c

    r15253 r41831  
    3636#include "psLogMsg.h"
    3737#include "psPolynomial.h"
     38#include "psAbort.h"
    3839#include "psAssert.h"
    3940
     
    203204
    204205
     206/** This function calculates the appropriate scaling factors needed to normalize the
     207 * input vector to the range -1 : +1.  These are stored on the polynomial in the given
     208 * direction.
     209 */
     210bool psChebyshevSetScale (psPolynomial2D* myPoly, const psVector *vec, int dir) {
     211
     212    psAssert ((dir == 0) || (dir == 1), "invalid direction %d\n", dir);
     213
     214    // find the min and max of the vector
     215    psF64 minValue = NAN;
     216    psF64 maxValue = NAN;
     217
     218    for (int i = 0; i < vec->n; i++) {
     219        if (isnan(vec->data.F64[i])) continue;
     220        if (isnan(minValue)) { minValue = vec->data.F64[i]; }
     221        if (isnan(maxValue)) { maxValue = vec->data.F64[i]; }
     222        minValue = PS_MIN(minValue, vec->data.F64[i]);
     223        maxValue = PS_MAX(maxValue, vec->data.F64[i]);
     224    }
     225    if (minValue == maxValue) {
     226        psWarning ("insufficient data range to determine scale factors\n");
     227        return false;
     228    }
     229
     230    myPoly->scale[dir] = 2.0 / (maxValue - minValue);
     231    myPoly->zero[dir]  = 1 - myPoly->scale[dir] * maxValue;
     232    return true;
     233}
     234
     235/** This function generates a normalized vector in the range -1 : +1 based on the input
     236    vector using the scale factors stored in myPoly in the given direction.
     237 */
     238psVector *psChebyshevNormVector (const psPolynomial2D* myPoly, const psVector *vec, int dir) {
     239
     240    psVector *norm = psVectorAlloc (vec->n, PS_TYPE_F64);
     241
     242    if (vec->type.type == PS_TYPE_F64) {
     243        for (int i = 0; i < vec->n; i++) {
     244            norm->data.F64[i] = vec->data.F64[i]*myPoly->scale[dir] + myPoly->zero[dir];
     245        }
     246        return norm;
     247    }
     248    if (vec->type.type == PS_TYPE_F32) {
     249        for (int i = 0; i < vec->n; i++) {
     250            norm->data.F64[i] = vec->data.F32[i]*myPoly->scale[dir] + myPoly->zero[dir];
     251        }
     252        return norm;
     253    }
     254
     255    psError(PS_ERR_UNKNOWN, true, "invalid type for chebyshev polynomial");
     256    return NULL;
     257}
     258
     259/** This function generates a vector containing the values of a Chebyshev polynomial of
     260    the given order evaluated at the coordinates given by the input vector, i.e., this
     261    function returns the vector T^n (x_i) where x_i is the input vector of values and n is
     262    the polynomial order.
     263 */
     264psVector *psChebyshevPolyVector (const psVector *vec, int order) {
     265
     266    if (order > 9) {
     267        psWarning ("Chebyshev orders higher than 9 are not yet coded\n");
     268        return NULL;
     269    }
     270
     271    psVector *out = psVectorAlloc (vec->n, PS_TYPE_F64);
     272
     273    // easy but non-general implementation
     274    switch (order) {
     275      case 0:
     276        for (int i = 0; i < vec->n; i++) {                                                   out->data.F64[i] = 1.0; } break;
     277      case 1:
     278        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i];                       out->data.F64[i] = x; } break;
     279      case 2:
     280        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = 2.0*x2 - 1.0; } break;
     281      case 3:
     282        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = x*(4.0*x2 - 3.0); } break;
     283      case 4:
     284        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = x2*(8.0*x2 - 8.0) + 1.0; } break;
     285      case 5:
     286        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = x *(x2*(16.0*x2 - 20.0) + 5.0); } break;
     287      case 6:
     288        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = x2*(x2*(32.0*x2 - 48.0) + 18.0) - 1.0; } break;
     289      case 7:
     290        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = x *(x2*(x2*(64.0*x2 - 112.0) + 56.0) - 7.0); } break;
     291      case 8:
     292        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = x2*(x2*(x2*(128.0*x2 - 256.0) + 160.0) - 32.0) + 1.0; } break;
     293      case 9:
     294        for (int i = 0; i < vec->n; i++) { psF64 x = vec->data.F64[i]; psF64 x2 = PS_SQR(x); out->data.F64[i] = x *(x2*(x2*(x2*(256.0*x2 - 576.0) + 432.0) - 129.0) + 9.0); } break;
     295      default:
     296        psWarning ("Chebyshev orders higher than 9 are not yet coded\n");
     297        psFree (out);
     298        return NULL;
     299    }
     300
     301    return out;
     302}
     303
    205304/*****************************************************************************
    206305    Polynomial coefficients will be accessed in [w][x][y][z] fashion.
     
    234333
    235334// XXX: You can do this without having to psAlloc() vector d.
    236 // XXX: How does the mask vector effect Crenshaw's formula?
     335// XXX: How does the mask vector affect Clenshaw's formula?
    237336// NOTE: We assume that x is scaled between -1.0 and 1.0;
    238337// XXX: Create a faster version for low-order Chebyshevs.
     
    343442                                  const psPolynomial2D* poly)
    344443{
    345     PS_ASSERT_DOUBLE_WITHIN_RANGE(x, -1.0, 1.0, 0.0);
    346     PS_ASSERT_DOUBLE_WITHIN_RANGE(y, -1.0, 1.0, 0.0);
     444  // XXX transform x,y to chebyshev range
     445  // PS_ASSERT_DOUBLE_WITHIN_RANGE(x, -1.0, 1.0, 0.0);
     446  // PS_ASSERT_DOUBLE_WITHIN_RANGE(y, -1.0, 1.0, 0.0);
    347447    PS_ASSERT_POLY_NON_NULL(poly, NAN);
    348448
     
    354454    unsigned int maxChebyPoly = 0;
    355455
     456    psF64 xNorm = x*poly->scale[0] + poly->zero[0];
     457    psF64 yNorm = y*poly->scale[1] + poly->zero[1];
     458
    356459    // Determine how many Chebyshev polynomials
    357460    // are needed, then create them.
     
    366469            if (!(poly->coeffMask[loop_x][loop_y] & PS_POLY_MASK_SET)) {
    367470                polySum += poly->coeff[loop_x][loop_y] *
    368                            psPolynomial1DEval(chebPolys[loop_x], x) *
    369                            psPolynomial1DEval(chebPolys[loop_y], y);
     471                           psPolynomial1DEval(chebPolys[loop_x], xNorm) *
     472                           psPolynomial1DEval(chebPolys[loop_y], yNorm);
    370473            }
    371474        }
     
    603706    }
    604707
     708    // scale & zero are used for Chebyshev polynomials to define the relationship between
     709    // the independent variables and the normalized version with range -1 : +1.  These
     710    // must be determined for a specific data set.
     711    newPoly->scale[0] = NAN;
     712    newPoly->zero[0]  = NAN;
     713
    605714    return(newPoly);
    606715}
     
    638747            newPoly->coeffMask[x][y] = PS_POLY_MASK_NONE;
    639748        }
     749    }
     750
     751    // scale & zero are used for Chebyshev polynomials to define the relationship between
     752    // the independent variables and the normalized version with range -1 : +1.  These
     753    // must be determined for a specific data set.
     754    for (int i = 0; i < 2; i++) {
     755      newPoly->scale[i] = NAN;
     756      newPoly->zero[i]  = NAN;
    640757    }
    641758
     
    756873            }
    757874        }
     875    }
     876
     877    // scale & zero are used for Chebyshev polynomials to define the relationship between
     878    // the independent variables and the normalized version with range -1 : +1.  These
     879    // must be determined for a specific data set.
     880    for (int i = 0; i < 3; i++) {
     881      newPoly->scale[i] = NAN;
     882      newPoly->zero[i]  = NAN;
    758883    }
    759884
     
    820945    }
    821946
     947    // scale & zero are used for Chebyshev polynomials to define the relationship between
     948    // the independent variables and the normalized version with range -1 : +1.  These
     949    // must be determined for a specific data set.
     950    for (int i = 0; i < 4; i++) {
     951      newPoly->scale[i] = NAN;
     952      newPoly->zero[i]  = NAN;
     953    }
     954
    822955    return(newPoly);
    823956}
     
    841974
    842975// this function must accept F32 and F64 input x vectors
     976// EAM XXX these functions seem inefficiently implemented with many nested function calls.
     977// they might benefit from unrolling.
    843978psVector *psPolynomial1DEvalVector(const psPolynomial1D *poly,
    844979                                   const psVector *x)
     
    8881023}
    8891024
     1025psVector *psPolynomial2DEvalChebVector(const psPolynomial2D *poly,
     1026                                       const psVector *x,
     1027                                       const psVector *y)
     1028{
     1029
     1030    if (!isfinite(poly->scale[0]) || !isfinite(poly->zero[0]) || !isfinite(poly->scale[1]) || !isfinite(poly->zero[1])) {
     1031        // re-calculate if not already determined? 
     1032        psError(PS_ERR_UNKNOWN, true, "normalization scales are not set for chebyshev polynomial");
     1033        return (NULL);
     1034    }
     1035
     1036    // Number of polynomial terms
     1037    int nXterm = 1 + poly->nX;      // Number of terms in x
     1038    int nYterm = 1 + poly->nY;      // Number of terms in y
     1039    if (nXterm > 9) {
     1040        psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit: orders higher than 9 are not yet coded\n");
     1041        return NULL;
     1042    }
     1043    if (nYterm > 9) {
     1044        psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit: orders higher than 9 are not yet coded\n");
     1045        return NULL;
     1046    }
     1047
     1048    // Generate normalized vectors for the range -1 : +1.  These functions cast to psF64
     1049    psVector *xNorm = psChebyshevNormVector (poly, x, 0);
     1050    psVector *yNorm = psChebyshevNormVector (poly, y, 1);
     1051   
     1052    // Generate the N cheb polynomials based on xNorm, yNorm
     1053    psArray *xPolySet = psArrayAlloc (nXterm);
     1054    for (int i = 0; i < nXterm; i++) {
     1055        xPolySet->data[i] = psChebyshevPolyVector (xNorm, i);
     1056    }
     1057    psArray *yPolySet = psArrayAlloc (nYterm);
     1058    for (int i = 0; i < nYterm; i++) {
     1059        yPolySet->data[i] = psChebyshevPolyVector (yNorm, i);
     1060    }
     1061
     1062    psVector *out = psVectorAlloc (x->n, PS_TYPE_F64);
     1063
     1064    psF64 *xData = xNorm->data.F64;
     1065    psF64 *yData = yNorm->data.F64;
     1066    psF64 *fData = out->data.F64;
     1067
     1068    // loop over all elements of the data vector
     1069    for (int i = 0; i < x->n; i++) {
     1070
     1071        if (!finite(xData[i])) {fData[i] = NAN; continue; }
     1072        if (!finite(yData[i])) {fData[i] = NAN; continue; }
     1073
     1074        psF64 sum = 0.0;
     1075        for (int jx = 0; jx < nXterm; jx++) {
     1076            psVector *jxCheb = xPolySet->data[jx];
     1077            for (int jy = 0; jy < nYterm; jy++) {
     1078                psVector *jyCheb = yPolySet->data[jy];
     1079                sum += poly->coeff[jx][jy] * jxCheb->data.F64[i] * jyCheb->data.F64[i];
     1080            }
     1081        }
     1082        fData[i] = sum;
     1083    }
     1084
     1085    psFree (xPolySet);
     1086    psFree (yPolySet);
     1087    psFree (xNorm);
     1088    psFree (yNorm);
     1089
     1090    return out;
     1091}
     1092
    8901093// this function must support input data types of F32 and F64
    8911094// all input vectors data types must match (all F32 or all F64)
     
    9011104    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(y, NULL);
    9021105
    903     psVector *tmp;
    904     unsigned int vecLen=x->n;
     1106    unsigned int vecLen = x->n;
     1107
     1108    // input vector types must match
     1109    if (y->type.type != x->type.type) {
     1110        psError(PS_ERR_UNKNOWN, true, "type mismatch in data vectors");
     1111        return (NULL);
     1112    }
    9051113
    9061114    // Determine the length of the output vector to by the minimum of the x,y vectors
    907     if (y->n < vecLen) {
    908         vecLen = y->n;
     1115    // XXX shouldn't we require x & y to have the same length?  seems meaningless otherwise
     1116    if (y->n != vecLen) {
     1117        psError(PS_ERR_UNKNOWN, true, "length mismatch in data vectors");
     1118        return (NULL);
     1119    }
     1120
     1121    if (poly->type == PS_POLYNOMIAL_CHEB) {
     1122        psVector *out = psPolynomial2DEvalChebVector (poly, x, y);
     1123        return out;
    9091124    }
    9101125
    9111126    switch (x->type.type) {
    912     case PS_TYPE_F32:
    913         if (y->type.type != x->type.type) {
    914             psError(PS_ERR_UNKNOWN, true, "type mismatch in data vectors");
    915             return (NULL);
    916         }
    917 
    918         // Create output vector to return
    919         tmp = psVectorAlloc(vecLen, PS_TYPE_F32);
    920 
    921         // Evaluate the polynomial at the specified points
    922         for (unsigned int i=0; i<vecLen; i++) {
    923             tmp->data.F32[i] = psPolynomial2DEval(poly,x->data.F32[i],y->data.F32[i]);
    924         }
    925         break;
    926     case PS_TYPE_F64:
    927         if (y->type.type != x->type.type) {
    928             psError(PS_ERR_UNKNOWN, true, "type mismatch in data vectors");
    929             return (NULL);
    930         }
    931 
    932         // Create output vector to return
    933         tmp = psVectorAlloc(vecLen, PS_TYPE_F64);
    934 
    935         // Evaluate the polynomial at the specified points
    936         for (unsigned int i=0; i<vecLen; i++) {
    937             tmp->data.F64[i] = psPolynomial2DEval(poly,x->data.F64[i],y->data.F64[i]);
    938         }
    939         break;
    940     default:
     1127      case PS_TYPE_F32: {
     1128          // Create output vector to return
     1129          psVector *out = psVectorAlloc(vecLen, PS_TYPE_F32);
     1130
     1131          // Evaluate the polynomial at the specified points
     1132          for (unsigned int i = 0; i < vecLen; i++) {
     1133              out->data.F32[i] = psPolynomial2DEval(poly,x->data.F32[i],y->data.F32[i]);
     1134          }
     1135          return out;
     1136      }
     1137      case PS_TYPE_F64: {
     1138          // Create output vector to return
     1139          psVector *out = psVectorAlloc(vecLen, PS_TYPE_F64);
     1140
     1141          // Evaluate the polynomial at the specified points
     1142          for (unsigned int i = 0; i < vecLen; i++) {
     1143              out->data.F64[i] = psPolynomial2DEval(poly,x->data.F64[i],y->data.F64[i]);
     1144          }
     1145          return out;
     1146      }
     1147      default:
    9411148        psError(PS_ERR_UNKNOWN, false, "invalid input data type.\n");
    9421149        return (NULL);
    9431150    }
    944     // Return output vector
    945     return(tmp);
     1151    psAbort ("impossible");
     1152    return NULL;
    9461153}
    9471154
Note: See TracChangeset for help on using the changeset viewer.