- Timestamp:
- Oct 11, 2021, 11:33:33 AM (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/eam_branches/ipp-dev-20210817/psLib/src/math/psPolynomial.c
r15253 r41831 36 36 #include "psLogMsg.h" 37 37 #include "psPolynomial.h" 38 #include "psAbort.h" 38 39 #include "psAssert.h" 39 40 … … 203 204 204 205 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 */ 210 bool 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 */ 238 psVector *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 */ 264 psVector *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 205 304 /***************************************************************************** 206 305 Polynomial coefficients will be accessed in [w][x][y][z] fashion. … … 234 333 235 334 // 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? 237 336 // NOTE: We assume that x is scaled between -1.0 and 1.0; 238 337 // XXX: Create a faster version for low-order Chebyshevs. … … 343 442 const psPolynomial2D* poly) 344 443 { 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); 347 447 PS_ASSERT_POLY_NON_NULL(poly, NAN); 348 448 … … 354 454 unsigned int maxChebyPoly = 0; 355 455 456 psF64 xNorm = x*poly->scale[0] + poly->zero[0]; 457 psF64 yNorm = y*poly->scale[1] + poly->zero[1]; 458 356 459 // Determine how many Chebyshev polynomials 357 460 // are needed, then create them. … … 366 469 if (!(poly->coeffMask[loop_x][loop_y] & PS_POLY_MASK_SET)) { 367 470 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); 370 473 } 371 474 } … … 603 706 } 604 707 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 605 714 return(newPoly); 606 715 } … … 638 747 newPoly->coeffMask[x][y] = PS_POLY_MASK_NONE; 639 748 } 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; 640 757 } 641 758 … … 756 873 } 757 874 } 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; 758 883 } 759 884 … … 820 945 } 821 946 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 822 955 return(newPoly); 823 956 } … … 841 974 842 975 // 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. 843 978 psVector *psPolynomial1DEvalVector(const psPolynomial1D *poly, 844 979 const psVector *x) … … 888 1023 } 889 1024 1025 psVector *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 890 1093 // this function must support input data types of F32 and F64 891 1094 // all input vectors data types must match (all F32 or all F64) … … 901 1104 PS_ASSERT_VECTOR_TYPE_F32_OR_F64(y, NULL); 902 1105 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 } 905 1113 906 1114 // 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; 909 1124 } 910 1125 911 1126 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: 941 1148 psError(PS_ERR_UNKNOWN, false, "invalid input data type.\n"); 942 1149 return (NULL); 943 1150 } 944 // Return output vector945 return (tmp);1151 psAbort ("impossible"); 1152 return NULL; 946 1153 } 947 1154
Note:
See TracChangeset
for help on using the changeset viewer.
