1 #include "NKM_SurfPack.hpp"
2 #include <cmath>
3
4 //the purpose of this file is to contain generic functions usable by everything
5
6 //#define __SURFPACK_ERR_CHECK__
7 //#define __SURFPACK_UNIT_TEST__
8
9 namespace nkm {
10
11 //not sure which of these usings are necessary, achieve compilation by random guessing
12 using std::fabs;
13 using std::pow;
14 using std::cerr;
15 using std::endl;
16 using std::ifstream;
17 using std::istream;
18 using std::ios;
19 //using std::numeric_limits;
20 using std::ofstream;
21 using std::ostream;
22 using std::setw;
23 using std::string;
24 //using std::vector;
25
if_close_enough(double a,double b)26 int if_close_enough(double a, double b)
27 {
28 // if(std::fabs(a-b)>1.0e-5){
29 // std::printf("a=%20.14f b=%20.14f\n",a,b); std::fflush(stdout);
30 // assert((std::fabs(a-b)<=1.0e-5));
31 // }
32 return (std::fabs(a-b)<=1.0e-5);
33 };
34
35 ///this function should be moved to surfpack.cpp
nchoosek(int n,int k)36 int nchoosek(int n, int k){
37 int nck=1;
38
39 int nloop=(k<n-k)?k:n-k;
40 if(nloop>0) {
41 nck=n;
42 for(int iloop=1; iloop<nloop; iloop++)
43 nck=(nck*(n-iloop))/(iloop+1);
44 }
45
46 return nck;
47 }
48
49 ///this function should be moved to surfpack.cpp
gen_rot_mat(MtxDbl & Rot,const MtxDbl & EulAng,int nvarsr)50 MtxDbl& gen_rot_mat(MtxDbl& Rot, const MtxDbl& EulAng, int nvarsr){
51 #ifdef __SURFPACK_ERR_CHECK__
52 assert((EulAng.getNRows()==(nvarsr*(nvarsr-1)/2))&&(EulAng.getNCols()==1));
53 #endif
54 MtxDbl I(nvarsr,nvarsr), R(nvarsr,nvarsr), RotTemp(nvarsr,nvarsr);
55 I.zero();
56 int ivarr;
57 for(ivarr=0; ivarr<nvarsr; ivarr++)
58 I(ivarr,ivarr)=1.0;
59 Rot=I;
60 int nang=nvarsr, iang, Iang=0;
61 double c, s;
62 for(ivarr=0;ivarr<nvarsr-1;ivarr++) {
63 nang--;
64 for(iang=0; iang<nang; iang++) {
65 c=std::cos(EulAng(Iang,0));
66 s=std::sin(EulAng(Iang,0));
67 R=I;
68 R(iang ,iang )= c;
69 R(iang ,iang+1)=-s;
70 R(iang+1,iang )= s;
71 R(iang+1,iang+1)= c;
72 matrix_mult(RotTemp,Rot,R,0,1.0);
73 Rot=RotTemp;
74 Iang++;
75 }
76 }
77 return Rot;
78 }
79
80 //all coordinates are between -1 and 1
gen_rand_rot_mat(MtxDbl & rot,int nvarsr)81 MtxDbl& gen_rand_rot_mat(MtxDbl& rot,int nvarsr)
82 {
83 int n_eul_ang=nchoosek(nvarsr, 2);
84 //printf("n_eul_ang=%d\n",n_eul_ang);
85 MtxDbl eul_ang(n_eul_ang,1);
86 double pi=2.0*std::acos(0.0);
87 int mymod = 1048576; //2^20 instead of 10^6 to be kind to the computer
88 for(int i=0; i<n_eul_ang; ++i)
89 eul_ang(i,0)=(std::rand() % mymod)*pi/mymod;
90 rot.newSize(nvarsr,nvarsr);
91 gen_rot_mat(rot, eul_ang, nvarsr);
92 return rot;
93 }
94
95
96 ///generates 2*nvarsr random samples between 0 and 1, the sample design is stored in a matrix with nvarsr rows and 2*nvars columns (one point per column), the sample design is binning optimal with the bins being chosen as the end points of a randomly rotated set of axes (so the BINS, but not the points, are maximin spaced) the design is NOT a latin hypercube and it is not symmetric, opposite octants are stored in sequential columns of the xr matrix.
gen_rand_axis_bin_opt_samples_0to1(MtxDbl & xr,int nvarsr)97 MtxDbl& gen_rand_axis_bin_opt_samples_0to1(MtxDbl& xr, int nvarsr)
98 {
99 gen_rand_rot_mat(xr,nvarsr);
100 xr.resize(nvarsr,2*nvarsr);
101 int mymod = 1048576; //2^20 instead of 10^6 to be kind to the computer
102 for(int j=nvarsr-1; j>=0; --j) {
103 //printf("surfpack.cpp: i=%d",i);
104 for(int i=0; i<nvarsr; ++i) {
105 //printf(" j=%d",j); fflush(stdout);
106 xr(i,2*j )=2.0*std::floor(1.0+xr(i,j))-1.0;
107 xr(i,2*j+1)=0.5*((-xr(i,2*j)*(std::rand() % mymod))/mymod+1.0);
108 xr(i,2*j )=0.5*(( xr(i,2*j)*(std::rand() % mymod))/mymod+1.0);
109 }
110 //printf("\n");
111 }
112 return xr;
113 }
114
115 /***********************************************************************/
116 /// num_multi_dim_poly_coef(int Nvarsr, int Ndeg) is for use with multi_dim_poly_power(), says how many coefficients are needed for a Nvarsr-dimensional polynomial "of degree abs(Ndeg)" if Ndeg>0 the polynomial contains all multidimensional monomials UPTO (inclusive) degree Ndeg, if Ndeg<0 it contains only those multidimensional monomials OF EXACT DEGREE abs(Ndeg)
num_multi_dim_poly_coef(int Nvarsr,int Ndeg)117 int num_multi_dim_poly_coef(int Nvarsr, int Ndeg){
118 int Npoly;
119 if(Ndeg<0)
120 Npoly=nchoosek(-Ndeg-1+Nvarsr,-Ndeg);
121 else
122 Npoly=nchoosek(Ndeg+Nvarsr,Ndeg);
123 return Npoly;
124 }
125
126 /***********************************************************************/
127 /**** multi_dim_poly_power() is a rather long function so I gave it ****/
128 /**** this special "boundary" comment so you can easily tell where ****/
129 /**** it begins and ends ****/
130 /***********************************************************************/
131 ///poly=multi_dim_poly_power(poly,Nvarsr,Ndeg,istart,jstart) returns a matrix of size={Npoly,Nvarsr}; if Ndeg>=0 this function returns the mixed partial powers of all Nvarsr-dimensional polynomials of degree less than or equal to zero (there are Npoly=nchoosek(Ndeg+Nvarsr,Ndeg) of these), if Ndeg<=0 this function returns the mixed partial powers of all Nvarsr-dimensional polynomials of exactly degree abs(Ndeg) (there are Npoly=nchoosek(abs(Ndeg)-1+Nvarsr,abs(Ndeg)) of these); istart and jstart are offsets from the beginning of the matrix that say where to start the next "group" of powers (istart and jstart are there to avoid needing to RECURSIVELY allocate, fill, copy contents of this "poly" to the next larger "poly" and then deallocate this poly (it's a performance/speed thing), when any function other than multi_dim_poly_power() calls multi_dim_poly_power(), istart and jstart should be left unspecified, which makes them default to zero; if the "user" specifies non-zero istart and jstart then it "voids the warranty" on multi_dim_poly_power(), specifically you could end up going beyond the bounds of poly (a memory error)
multi_dim_poly_power(MtxInt & poly,int Nvarsr,int Ndeg,int istart,int jstart,int iffirst)132 MtxInt& multi_dim_poly_power(MtxInt& poly, int Nvarsr, int Ndeg, int istart, int jstart, int iffirst){
133 //printf("istart=%d jstart=%d iffirst=%d Nvarsr=%d Ndeg=%d poly.NRows()=%d\n",istart,jstart,iffirst,Nvarsr,Ndeg,poly.getNRows());
134 int Npoly, npoly;
135 #ifdef __SURFPACK_ERR_CHECK__
136 int jstartorig=jstart;
137 #endif
138 //determine the total number of polynomials that this call of multi_dim_poly_power() is supposed to find mixed partial powers for
139 if(Ndeg<0)
140 Npoly=nchoosek(-Ndeg-1+Nvarsr,-Ndeg);
141 else
142 Npoly=nchoosek(Ndeg+Nvarsr,Ndeg);
143
144 //istart=jstart=0 (the default when istart and jstart are not specified) is the flag for this function being called by the "user" (as opposed to a bigger multi_dim_poly_power()) and we don't want to require the user to know how big poly should be so we will right size it for him/her, if the user specified nonzero istart and/or jstart then it is his/her responsibility for making sure that poly is already big enough but since I'm a nice guy I put in an assert for when the user runs this in debug mode
145 if((istart==0)&&(jstart==0)&&(iffirst==1)) {
146 //printf("newSizing: Npoly=%d\n",Npoly);
147 poly.newSize(Nvarsr,Npoly);
148 }
149 else{
150 #ifdef __SURFPACK_ERR_CHECK__
151 if(!((jstart+Npoly<=poly.getNCols())&&(istart+Nvarsr<=poly.getNRows()))) {
152 printf("Error in multi_dim_poly_power(): you asked me to fill in mixed partial polynomial powers beyond the bounds of the MtxInt& poly that you gave me. If you want me to resize poly don't specify a nonzero istart or jstart\n, jstart=%d Npoly=%d poly.NCols=%d istart=%d Nvarsr=%d poly.NRows=%d\n",jstart,Npoly,poly.getNCols(),istart,Nvarsr,poly.getNRows());
153 assert((jstart+Npoly<=poly.getNCols())&&(istart+Nvarsr<=poly.getNRows()));
154 }
155 #endif
156 }
157
158 int ivar, j;
159 int ndeg; //this is for recursive calls to smaller multi_dim_poly_power()'s that's why it starts with little "n" instead of big "N"
160 if(Ndeg==0) {
161 // a Nvarsr-dimensional polynomial of total degree 0 has all mixed partial powers equal to zero, istart and jstart should be 0 but the user could have done something strange
162 for(ivar=0;ivar<Nvarsr; ivar++)
163 poly(istart+ivar,jstart)=0;
164 }
165 else if(Nvarsr==1) {
166 //this is a failsafe for direct user input, it isn't necessary for recursive calls
167 if(Ndeg>0){
168 //all powers less than or equal to Ndeg for 1 variable are
169 // [ 0; ...
170 // 1; ...
171 // 2; ...
172 // 3; ...
173 // .
174 // :
175 // Ndeg];
176 // make sure to offset by istart and jstart (it shouldn't be necessary but who knows the user might have actually specified istart and jstart)
177 for(j=0;j<=Ndeg;j++)
178 poly(istart,jstart+j)=j;
179 }
180 else{
181 //Ndeg<0 says you only want powers of exactly abs(Ndeg) for 1 variable this is [-Ndeg]; istart and jstart "should be" zero but who knows what the user gave us as input
182 poly(istart,jstart)=-Ndeg;
183 }
184 }
185 else if(Ndeg==-1) {
186 //Ndeg = -1 says you want all polynomials of EXACTLY degree 1, for which the "mixed" partial powers is just an identity matrix, but make sure to offset it by istart and jstart
187 for(j=0; j<Nvarsr; j++) {
188 for(ivar=0; ivar<Nvarsr; ivar++)
189 poly(istart+ivar,jstart+j)=0;
190 poly(istart+j,jstart+j)=1;
191 }
192 }
193 else if(Ndeg>0) {
194 //this logic should only be executed when this multi_dim_poly_power() is called by the "user" (it should not be executed when this multi_dim_poly_power() is called by a bigger multi_dim_poly_power()); Ndeg > 0 says "I want all polynomials of degree less than or equal to Ndeg" so we will do this in batches of exactly degree 0, exactly degree 1, exactly degree 2, ..., exactly degree Ndeg
195 for(ndeg=0; ndeg<=Ndeg; ndeg++) {
196 npoly=nchoosek(ndeg-1+Nvarsr,ndeg);
197 multi_dim_poly_power(poly, Nvarsr, -ndeg, istart, jstart, 0);
198 jstart+=npoly;
199 }
200 #ifdef __SURFPACK_ERR_CHECK__
201 assert(jstart-jstartorig==Npoly); //jstartorig should be zero but who knows what the user gave us
202 #endif
203 }
204 else if(Nvarsr==2) {
205 //we know that Ndeg < -1, so we want all 2-dimensional polynomials of exaclty degree abs(Ndeg) and the answer for 2 dimensions is easy
206 // [ abs(Ndeg) 0 ;...
207 // abs(Ndeg)-1 1 ;...
208 // abs(Ndeg)-2 2 ;...
209 // . . ;...
210 // : : ;...
211 // 2 abs(Ndeg)-2 ;...
212 // 1 abs(Ndeg)-1 ;...
213 // 0 abs(Ndeg) ]^T
214 for(j=0; j<=-Ndeg; j++) {
215 poly(istart ,jstart+j)=-Ndeg-j;
216 poly(istart+1,jstart+j)=j;
217 }
218 }
219 else{ //we know that Ndeg < -1 and Nvarsr > 2
220 //we want all Nvarsr-dimensional polynomials of exaclty degree abs(Ndeg) so we are going to start with the first row being ideg=abs(Ndeg) decrementing that and pairing it with ALL (Nvarsr-1)-dimensional polynomials of exactly degree ndeg=abs(Ndeg)-ideg
221 int nvarsr=Nvarsr-1; //this is for recursive calls to smaller multi_dim_poly_power()'s that's why it starts with little "n" instead of big "N"
222 for(int ideg=-Ndeg; ideg>=0; ideg--){
223 ndeg=-Ndeg-ideg;
224 npoly=nchoosek(ndeg-1+nvarsr,ndeg);
225 for(j=0; j<npoly; j++)
226 poly(istart,jstart+j)=ideg;
227 multi_dim_poly_power(poly,nvarsr,-ndeg,istart+1,jstart,0);
228 jstart+=npoly;
229 }
230 #ifdef __SURFPACK_ERR_CHECK__
231 assert(jstart-jstartorig==Npoly); //don't expect jstartorig to be zero
232 #endif
233 }
234
235 return poly;
236 }
237
238 /***********************************************************************/
239 /**** multi_dim_poly_power() ends here ****/
240 /***********************************************************************/
241
242 /// if ndeg>=0 generates a matrix "poly" of nvarsr-dimensional polynomial powers that for all polynomials WITHOUT MIXED POWERS up to (and including) degree ndeg. If ndeg<0 it generates a nvarsr by nvarsr diagonal matrix "poly" of non-mixed polynomials of exact degree abs(ndeg), this diagonal matrix has abs(ndeg) for all its diagonal elements.
main_effects_poly_power(MtxInt & poly,int nvarsr,int ndeg)243 MtxInt& main_effects_poly_power(MtxInt& poly, int nvarsr, int ndeg) {
244
245 #ifdef __SURFPACK_ERR_CHECK__
246 assert(nvarsr>0);
247 #endif
248
249 if(ndeg<0) {
250 int abs_ndeg=std::abs(ndeg);
251 poly.newSize(nvarsr,nvarsr);
252 poly.zero();
253 for(int ivarsr=0; ivarsr<nvarsr; ++ivarsr)
254 poly(ivarsr,ivarsr)=abs_ndeg;
255 return poly;
256 }
257 else if(ndeg==0) {
258 poly.newSize(nvarsr,1);
259 poly.zero();
260 return poly;
261 }
262
263 poly.newSize(nvarsr,1+nvarsr*ndeg);
264 poly.zero();
265 int ipoly=0;
266 for(int ideg=1; ideg<=ndeg; ++ideg)
267 for(int ivarsr=0; ivarsr<nvarsr; ++ivarsr)
268 poly(ivarsr,++ipoly)=ideg;
269
270 return poly;
271 }
272
273
poly_to_flypoly(MtxInt & flypoly,const MtxInt & poly,int maxorder)274 MtxInt& poly_to_flypoly(MtxInt& flypoly, const MtxInt& poly, int maxorder) {
275 int npoly=poly.getNCols();
276 int nvars=poly.getNRows();
277 flypoly.newSize(maxorder+1,npoly);
278 for(int ipoly=0; ipoly<npoly; ++ipoly) {
279 int nmult=0;
280 for(int ivar=0; ivar<nvars; ++ivar)
281 for(int i=0; i<poly(ivar,ipoly); ++i)
282 flypoly(++nmult,ipoly)=ivar;
283 flypoly(0,ipoly)=nmult;
284 #ifdef __SURFPACK_ERR_CHECK__
285 assert(nmult<=maxorder);
286 #endif
287 }
288 return flypoly;
289 }
290
291 //this function modifies flycoef as needed so make sure you pass in a COPY of
292 //the original coefficients instead of the original coefficients themselves
poly_der_to_flypoly(MtxInt & flypoly,MtxDbl & flycoef,const MtxInt & poly,const MtxInt & der,int ider,int maxorder)293 void poly_der_to_flypoly(MtxInt& flypoly, MtxDbl& flycoef, const MtxInt& poly,
294 const MtxInt& der, int ider, int maxorder) {
295 int npoly=poly.getNCols();
296 int nvars=poly.getNRows();
297 #ifdef __SURFPACK_ERR_CHECK__
298 assert((flycoef.getNRows()==npoly)&&(flycoef.getNCols()==1)&&
299 (der.getNRows()==nvars)&&(0<=ider)&&(ider<der.getNCols()));
300 #endif
301 flypoly.newSize(maxorder+1,npoly); //you really should have sized flypoly
302 //appropriately outside of this function for efficiency, this is just a
303 //fail safe, you shouldn't be using it
304
305 for(int ipoly=0; ipoly<npoly; ++ipoly) {
306 double tempcoef=flycoef(ipoly,0);
307 if(tempcoef==0.0)
308 flypoly(0,ipoly)=0;
309 else {
310 int nmult=0;
311 for(int ivar=0; ivar<nvars; ++ivar) {
312 //determine the derivative of this multidimensional monomial and
313 //store it as a flypoly representation
314 int thisder=der(ivar,ider);
315 int powafterder=poly(ivar,ipoly)-thisder;
316 if(powafterder<0) {
317 tempcoef=0.0;
318 nmult=0;
319 break; //break out of ivar loop
320 }
321 //since the derivative polynomial's coefficient isn't zero (so far)
322 //we need to know what it is
323 for(int jder=0; jder<thisder; ++jder)
324 tempcoef*=(poly(ivar,ipoly)-jder);
325
326 for(int i=0; i<powafterder; ++i)
327 flypoly(++nmult,ipoly)=ivar;
328 }
329 flycoef(ipoly,0)=tempcoef;
330 flypoly(0,ipoly)=nmult;
331 }
332 }
333 return;
334 }
335
336
evaluate_flypoly(MtxDbl & y,const MtxInt & flypoly,const MtxDbl & coef,const MtxDbl & xr)337 MtxDbl& evaluate_flypoly(MtxDbl& y, const MtxInt& flypoly, const MtxDbl& coef, const MtxDbl& xr) {
338 int npts=xr.getNCols();
339 int npoly=flypoly.getNCols();
340 #ifdef __SURFPACK_ERR_CHECK__
341 assert((0<npts)&&(0<xr.getNRows())&&(0<npoly)&&(0<flypoly.getNRows())&&
342 (npoly==coef.getNRows())&&(1==coef.getNCols()));
343 {
344 int maxorder=flypoly(0,0);
345 for(int i=1; i<npoly; ++i)
346 maxorder=(flypoly(0,i)<=maxorder)?maxorder:flypoly(0,i);
347 assert(maxorder<flypoly.getNRows());
348 }
349 #endif
350 y.newSize(1,npts);
351 for(int ipt=0; ipt<npts; ++ipt) {
352 double tempy=0.0;
353 for(int ipoly=0; ipoly<npoly; ++ipoly) {
354 int nmult=flypoly(0,ipoly);
355 double term=coef(ipoly,0);
356 for(int imult=1; imult<=nmult; ++imult)
357 term*=xr(flypoly(imult,ipoly),ipt);
358 tempy+=term;
359 }
360 y(0,ipt)=tempy;
361 }
362 return y;
363 }
364
evaluate_poly(MtxDbl & y,MtxInt & flypoly,const MtxInt & poly,const MtxDbl & coef,const MtxDbl & xr)365 MtxDbl& evaluate_poly(MtxDbl& y, MtxInt& flypoly, const MtxInt& poly, const MtxDbl& coef, const MtxDbl& xr) {
366 int nvars=poly.getNRows();
367 int npoly=poly.getNCols();
368 #ifdef __SURFPACK_ERR_CHECK__
369 assert((0<npoly)&&(0<nvars)&&(nvars==xr.getNRows()));
370 #endif
371 int maxorder=0;
372 for(int ipoly=0; ipoly<npoly; ++ipoly) {
373 int thisorder=poly(0,ipoly);
374 for(int ivar=1; ivar<nvars; ++ivar)
375 thisorder+=poly(ivar,ipoly);
376 maxorder=(thisorder<=maxorder)?maxorder:thisorder;
377 }
378 poly_to_flypoly(flypoly, poly, maxorder);
379 return evaluate_flypoly(y, flypoly, coef, xr);
380 }
381
382
evaluate_poly_der(MtxDbl & dy,MtxInt & flypoly,MtxDbl & flycoef,const MtxInt & poly,const MtxInt & der,const MtxDbl & coef,const MtxDbl & xr)383 MtxDbl& evaluate_poly_der(MtxDbl& dy, MtxInt& flypoly, MtxDbl& flycoef,
384 const MtxInt& poly, const MtxInt& der,
385 const MtxDbl& coef, const MtxDbl& xr) {
386 int nvars=poly.getNRows();
387 int npoly=poly.getNCols();
388 int npts =xr.getNCols();
389 int nder =der.getNCols();
390 #ifdef __SURFPACK_ERR_CHECK__
391 assert((0<nvars)&&(nvars==xr.getNRows())&&(nvars==der.getNRows())&&
392 (0<npoly)&&(npoly==coef.getNRows())&&(1==coef.getNCols())&&
393 (0<npts)&&(0<nder));
394 #endif
395
396 //determine the maximum total order of any multidimensional monomial in poly
397 //so that we know how many rows flypoly will need.
398 int maxorder=0;
399 for(int ipoly=0; ipoly<npoly; ++ipoly) {
400 int thisorder=poly(0,ipoly);
401 for(int ivar=1; ivar<nvars; ++ivar)
402 thisorder+=poly(ivar,ipoly);
403 maxorder=(thisorder<=maxorder)?maxorder:thisorder;
404 }
405
406
407 dy.newSize(nder,npts); //we want all the information (derivatives)
408 //associated with a point to be contiguous in memory (this means in the
409 //same column of the dy); The nkm::SurfMat templated matrix class
410 //uses Column Major Ordering (the same convention as MATLAB and FORTRAN)
411 //to facilitate the use of BLAS and LAPACK (which is written in FORTRAN)
412 //C++ normally uses Row Major Ordering (each column is contiguous, but
413 //rows are not);
414
415 for(int ider=0; ider<nder; ++ider) {
416 //we are deliberately not going to assign the result in contiguous
417 //order BECAUSE we only want to determine each derivative of the
418 //polynomial once, instead of once for each point, and we don't want
419 //to have to allocate enough memory to store all the derivatives of
420 //all the polynomials at the same time.
421
422 //determine the flypoly representation of the polynomial that is the
423 //der(:,ider) mixed multidimensional derivative of the polynomial in poly
424 flycoef.copy(coef); //need to copy coef so we don't modify the original
425 poly_der_to_flypoly(flypoly, flycoef, poly, der, ider, maxorder);
426
427
428 for(int ipt=0; ipt<npts; ++ipt) { //loop over points
429 double sumofterms=0.0; //this is workspace to compute/hold the
430 //current derivative at the current point so we don't need to
431 //matrix access "dy" so many times
432 for(int ipoly=0; ipoly<npoly; ++ipoly) { //loop over multidimensional
433 //monomials in the derivative polynomial
434 double term=flycoef(ipoly,0); //this is workspace to evaluate
435 //the current multidimensional monomial
436 int nmult=flypoly(0,ipoly); //this is the total order of the current
437 //multidimensional monomial
438 for(int imult=1; imult<=nmult; ++imult)
439 term*=xr(flypoly(imult,ipoly),ipt);
440 sumofterms+=term;
441 }
442 dy(ider,ipt)=sumofterms;
443 }
444 }
445 return dy;
446 }
447
448
449
450 //this function assumes that flypoly (the fast and "on the fly" polynomial representation) is already known and uses it to evaluate g at xr.
evaluate_flypoly_basis(MtxDbl & g,const MtxInt & flypoly,const MtxDbl & xr)451 MtxDbl& evaluate_flypoly_basis(MtxDbl& g, const MtxInt& flypoly, const MtxDbl& xr) {
452 int npts=xr.getNCols();
453 int npoly=flypoly.getNCols();
454 #ifdef __SURFPACK_ERR_CHECK__
455 assert((0<npts)&&(0<xr.getNRows())&&(0<npoly)&&(0<flypoly.getNRows()));
456 {
457 int maxorder=flypoly(0,0);
458 for(int i=1; i<npoly; ++i)
459 maxorder=(flypoly(0,i)<=maxorder)?maxorder:flypoly(0,i);
460 assert(maxorder<flypoly.getNRows());
461 }
462 #endif
463 g.newSize(npoly,npts);
464 for(int ipt=0; ipt<npts; ++ipt)
465 for(int ipoly=0; ipoly<npoly; ++ipoly) {
466 int nmult=flypoly(0,ipoly);
467 double tempg=1.0;
468 for(int imult=1; imult<=nmult; ++imult)
469 tempg*=xr(flypoly(imult,ipoly),ipt);
470 g(ipoly,ipt)=tempg;
471 }
472 return g;
473 }
474
475 //this function determines flypoly (the "on the fly" polynomial representation) from poly and then evaluates g at xr using flypoly
evaluate_poly_basis(MtxDbl & g,MtxInt & flypoly,const MtxInt & poly,const MtxDbl & xr)476 MtxDbl& evaluate_poly_basis(MtxDbl& g, MtxInt& flypoly, const MtxInt& poly, const MtxDbl& xr) {
477 int nvars=poly.getNRows();
478 int npoly=poly.getNCols();
479 #ifdef __SURFPACK_ERR_CHECK__
480 assert(nvars==xr.getNRows());
481 #endif
482 int maxorder=0;
483 for(int ipoly=0; ipoly<npoly; ++ipoly) {
484 int thisorder=poly(0,ipoly);
485 for(int ivar=1; ivar<nvars; ++ivar)
486 thisorder+=poly(ivar,ipoly);
487 maxorder=(thisorder<=maxorder)?maxorder:thisorder;
488 }
489 poly_to_flypoly(flypoly, poly, maxorder);
490 return evaluate_flypoly_basis(g, flypoly, xr);
491 }
492
493
494
evaluate_poly_der_basis(MtxDbl & dg,MtxInt & flypoly,MtxDbl & flycoef,const MtxInt & poly,const MtxInt & der,const MtxDbl & xr)495 MtxDbl& evaluate_poly_der_basis(MtxDbl& dg, MtxInt& flypoly, MtxDbl& flycoef,
496 const MtxInt& poly, const MtxInt& der,
497 const MtxDbl& xr) {
498 int nvars=poly.getNRows();
499 int npoly=poly.getNCols();
500 int nder=der.getNCols();
501 int npts=xr.getNCols();
502 #ifdef __SURFPACK_ERR_CHECK__
503 assert((0<nvars)&&(nvars==der.getNRows())&&(nvars==xr.getNRows())&&
504 (0<npoly)&&(0<nder)&&(0<npts));
505 #endif
506
507 int maxorder=0;
508 for(int ipoly=0; ipoly<npoly; ++ipoly) {
509 int thisorder=poly(0,ipoly);
510 for(int ivar=1; ivar<nvars; ++ivar)
511 thisorder+=poly(ivar,ipoly);
512 maxorder=(thisorder<=maxorder)?maxorder:thisorder;
513 }
514
515 flycoef.newSize(npoly,1);
516 int ncol=nder*npts;
517 dg.newSize(npoly,ncol); //layout in memory: polynomial is contigous
518 //(i.e. a single column), derivatives are outside that, points are outside
519 //that. If der was a gradient, then using matlab notation...
520 //dy=reshape(coef'*dg,nder,npts) would be a matrix with a gradient in each
521 //column and different columns being different points.
522
523 for(int ider=0; ider<nder; ++ider) {
524 //we are deliberately not going to assign the result in contiguous
525 //order BECAUSE we only want to determine each derivative of the
526 //polynomial once, instead of once for each point, and we don't want
527 //to have to allocate enough memory to store all the derivatives of
528 //all the polynomials at the same time.
529
530 //determine the flypoly representation of the polynomial that is the
531 //der(:,ider) mixed multidimensional derivative of the polynomial in poly
532 for(int ipoly=0; ipoly<npoly; ++ipoly)
533 flycoef(ipoly,0)=1.0;
534 poly_der_to_flypoly(flypoly, flycoef, poly, der, ider, maxorder);
535
536 int ipt, icol;
537 for(ipt=0, icol=ider; ipt<npts; ++ipt, icol+=nder)
538 for(int ipoly=0; ipoly<npoly; ++ipoly) {
539 double tempdg=flycoef(ipoly,0);
540 int nmult=flypoly(0,ipoly);
541 for(int imult=1; imult<=nmult; ++imult)
542 tempdg*=xr(flypoly(imult,ipoly),ipt);
543 dg(ipoly,icol)=tempdg;
544 }
545 }
546 return dg;
547 }
548
549
550
551
552
553
554 /// Throw an exception if end-of-file has been reached
checkForEOF(istream & is)555 void surfpack::checkForEOF(istream& is)
556 {
557 if (is.eof()) {
558 throw surfpack::io_exception("End of file reached unexpectedly.");
559 }
560 }
561
562 /// Return true if the file specified by parameter file name has the extension specified by parameter extension
hasExtension(const string & filename,const string extension)563 bool surfpack::hasExtension(const string& filename, const string extension)
564 {
565 return (filename.find(extension) == filename.size() - extension.size());
566 }
567
568 } // end namespace nkm
569
570 #ifdef __SURFPACK_UNIT_TEST__
571 //needs surf77_config.h surfpack_LAPACK_wrappers.h surfpack_system_headers.h everything else is in the nkm directory
572 //g++ -o NKM_SurfPack_UnitMain NKM_SurfPack.cpp NKM_SurfMat.o /usr/lib64/libblas.so /usr/lib64/liblapack.so
573
574 using namespace nkm;
main()575 int main(){
576 for(int n=0; n<5; n++)
577 for(int k=0; k<=n; k++)
578 printf("nchoosek(%d,%d)=%d\n",n,k,nchoosek(n,k));
579
580 MtxInt poly;
581 multi_dim_poly_power(poly,4,4);
582 int npoly=poly.getNCols();
583 int nvarsr=poly.getNRows();
584 printf("poly.getNCols()=%d\npoly=...\n",poly.getNCols());
585
586 for(int ipoly=0; ipoly<npoly; ipoly++) {
587 printf("%8d [",ipoly);
588 for(int ixr=0; ixr<nvarsr; ixr++)
589 printf(" %d",poly(ixr,ipoly));
590 printf(" ]^T\n");
591 }
592
593 double pi=std::acos(0.0)*2.0;
594 MtxDbl EulAng(6,1), Rot(4,4), RotShouldBe(4,4);
595 EulAng(0,0)=pi/6.0;
596 EulAng(1,0)=pi/4.0;
597 EulAng(2,0)=pi/3.0;
598 EulAng(3,0)=pi/6.0;
599 EulAng(4,0)=pi/4.0;
600 EulAng(5,0)=pi/3.0;
601 //This RotShouldBe was evaluated for these EulAng by matlab code that I know works
602 RotShouldBe(0,0)= -0.057800215120219;
603 RotShouldBe(1,0)= 0.353765877365274;
604 RotShouldBe(2,0)= 0.768283046242747;
605 RotShouldBe(3,0)= 0.530330085889911;
606 RotShouldBe(0,1)= -0.695272228311384;
607 RotShouldBe(1,1)= -0.649306566066328;
608 RotShouldBe(2,1)= 0.035320133098213;
609 RotShouldBe(3,1)= 0.306186217847897;
610 RotShouldBe(0,2)= 0.647692568794007;
611 RotShouldBe(1,2)= -0.414729655649473;
612 RotShouldBe(2,2)= -0.183012701892219;
613 RotShouldBe(3,2)= 0.612372435695795;
614 RotShouldBe(0,3)= -0.306186217847897;
615 RotShouldBe(1,3)= 0.530330085889911;
616 RotShouldBe(2,3)= -0.612372435695795;
617 RotShouldBe(3,3)= 0.500000000000000;
618 gen_rot_mat(Rot,EulAng,4);
619 printf("Rot=...\n");
620 for(int ixr=0; ixr<nvarsr; ixr++){
621 printf(" [ ");
622 for(int jxr=0; jxr<nvarsr; jxr++) {
623 printf("%9f ",Rot(ixr,jxr));
624 if(std::fabs(Rot(ixr,jxr)-RotShouldBe(ixr,jxr))>1.0e-15) {
625 printf("\n(%d,%d):Rot=%.16f RotShouldBe=%.16f\n",ixr,jxr,Rot(ixr,jxr),RotShouldBe(ixr,jxr));
626 fflush(stdout);
627 assert(Rot(ixr,jxr)==RotShouldBe(ixr,jxr));
628 }
629 }
630 printf("]\n");
631 }
632 printf("We know Rot is ok because it didn't assert(false)\n");
633
634 MtxDbl should_be_identity(4,4);
635 matrix_mult(should_be_identity,Rot,Rot,0.0,1.0,'N','T');
636 printf("should_be_identity=Rot*Rot^T=...\n");
637 for(int ixr=0; ixr<nvarsr; ixr++){
638 printf(" [ ");
639 for(int jxr=0; jxr<nvarsr; jxr++) {
640 printf("%9f ",should_be_identity(ixr,jxr));
641 }
642 printf("]\n");
643 }
644
645 matrix_mult(should_be_identity,Rot,Rot,0.0,1.0,'T','N');
646 printf("should_be_identity=Rot^T*Rot=...\n");
647 for(int ixr=0; ixr<nvarsr; ixr++){
648 printf(" [ ");
649 for(int jxr=0; jxr<nvarsr; jxr++) {
650 printf("%9f ",should_be_identity(ixr,jxr));
651 }
652 printf("]\n");
653 }
654
655
656 int rotnvarsr=5;
657 MtxDbl BaseAxis(rotnvarsr,2*rotnvarsr); BaseAxis.zero();
658 for(int ixr=0; ixr<rotnvarsr; ixr++) {
659 BaseAxis(ixr,ixr)=1.0;
660 BaseAxis(ixr,ixr+rotnvarsr)=-1.0;
661 }
662 MtxDbl Axis;
663
664 int noct=static_cast<int>(std::pow(2,rotnvarsr));
665 MtxInt InOct(noct,1); InOct.zero();
666 int NDV=(rotnvarsr*(rotnvarsr-1))/2; //nchoosek(nvarsr,2);
667 MtxDbl lowerBounds(NDV,1); lowerBounds.zero();
668 MtxDbl upperBounds(NDV,1);
669 int mymod=1048576; //2^20 instead of 10^6 to be kind to the computer
670 for(int jxr=0; jxr<NDV; jxr++) upperBounds(jxr,0)=pi;
671
672 int nguess=2*rotnvarsr*noct*100;
673
674 EulAng.newSize(NDV,1);
675 for(int iguess=0; iguess<nguess; iguess++) {
676 for(int jxr=0; jxr<NDV; jxr++)
677 EulAng(jxr,0)=(rand()%mymod)*upperBounds(jxr,0)/mymod;
678 gen_rot_mat(Rot,EulAng,rotnvarsr);
679 matrix_mult(Axis,Rot,BaseAxis,0.0,1.0,'T');
680 for(int k=0; k<2*rotnvarsr; k++){
681 int ioct=(Axis(0,k)>=0.0);
682 for(int jxr=1; jxr<rotnvarsr; jxr++)
683 ioct+=(Axis(jxr,k)>=0)*(static_cast<int>(std::pow(2.0,jxr)));
684 InOct(ioct,0)++;
685 }
686 }
687 printf("relative # of axis endpoints per octant\n");
688 for(int ioct=0; ioct<noct; ioct++)
689 printf(" InOct(%d/%d)=%g\n",ioct,noct,InOct(ioct,0)/(2.0*(nguess*rotnvarsr/noct)));
690
691 NDV=3;
692 int npts=2;
693 int ndeg=3;
694 int nder=6;
695 multi_dim_poly_power(poly,NDV,ndeg);
696 npoly=poly.getNCols();
697 assert(npoly==num_multi_dim_poly_coef(NDV,ndeg));
698
699
700 MtxDbl xr(NDV,npts);
701 MtxDbl y(1,npts), y2(1,npts), y3(1,npts), dy(nder,npts), dy2(1,nder*npts);
702 y3.zero();
703 for(int ixr=0; ixr<NDV; ++ixr) {
704 xr(ixr,0)=1.0;
705 xr(ixr,1)=ixr+1;
706 }
707
708 MtxDbl g(npoly,npts), g2(npoly,npts), dg(npoly,nder*npts);
709 MtxDbl coef1(npoly,1), coefs(npoly,1), flycoef;
710 MtxInt flypoly, flypoly2, der(NDV,nder);
711 der.zero();
712 der(0,1)=1;
713 der(1,2)=1;
714 der(2,3)=1;
715 der(1,4)=2;
716 der(0,5)=1;
717 der(1,5)=2;
718
719
720 poly_to_flypoly(flypoly,poly,ndeg);
721 //for(int ipoly=0; ipoly<npoly; ++ipoly) {
722 //for(int ifly=0; ifly<=ndeg; ++ifly)
723 // printf("%d ",flypoly(ifly,ipoly));
724 //printf("\n");
725 //}
726 //printf("\n");
727 evaluate_flypoly_basis(g, flypoly, xr);
728 evaluate_poly_basis(g2, flypoly2, poly, xr);
729 printf("Testing Polynomials\n");
730 double tempdouble;
731 for(int ipoly=0; ipoly<npoly; ++ipoly) {
732 coef1(ipoly,0)=1.0;
733 coefs(ipoly,0)=ipoly+1;
734 int ixr=0;
735 printf("g%d(x)=x%d^%d",ipoly,ixr,poly(ixr,ipoly));
736 for(ixr=1; ixr<NDV; ++ixr)
737 printf("*x%d^%d",ixr,poly(ixr,ipoly));
738 printf("=1");
739 int nmult=flypoly(0,ipoly);
740 assert(flypoly(0,ipoly)==flypoly2(0,ipoly));
741 for(int imult=1; imult<=nmult; ++imult) {
742 printf("*x%d",flypoly(imult,ipoly));
743 assert(flypoly(imult,ipoly)==flypoly2(imult,ipoly));
744 }
745 printf(";");
746 for(int ipt=0; ipt<npts; ++ipt) {
747 ixr=0;
748 printf(" g%d(%g",ipoly,xr(ixr,ipt));
749 tempdouble=coefs(ipoly,0)*std::pow(xr(ixr,ipt),poly(ixr,ipoly));
750 for(ixr=1; ixr<NDV; ++ixr) {
751 printf(",%g",xr(ixr,ipt));
752 tempdouble*=std::pow(xr(ixr,ipt),poly(ixr,ipoly));
753 }
754 printf(")=%g;",g(ipoly,ipt));
755 assert(g(ipoly,ipt)==g2(ipoly,ipt));
756 y3(0,ipt)+=tempdouble;
757 }
758 printf("\n");
759 }
760 matrix_mult(y,coefs,g,0.0,1.0,'T','N');
761 evaluate_poly(y2, flypoly, poly, coefs, xr);
762 assert((y.getNRows()==1)&&(y.getNCols()==npts)&&
763 (y2.getNRows()==1)&&(y2.getNCols()==npts)&&
764 (y3.getNRows()==1)&&(y3.getNCols()==npts));
765 for(int ipt=0; ipt<npts; ++ipt) {
766 printf("ipt=%d y=%g y2=%g y3=%g\n",ipt,y(0,ipt),y2(0,ipt),y3(0,ipt));
767 assert((y(0,ipt)==y3(0,ipt))&&(y2(0,ipt)==y3(0,ipt)));
768 }
769 printf("we know that polynomials evaluated correcty because it didn't fail the assert assert.\n");
770
771 printf("\n Begin Testing Polynomial Derivatives\n");
772 evaluate_poly_der(dy, flypoly, flycoef, poly, der, coef1, xr);
773 evaluate_poly_der_basis(dg, flypoly, flycoef, poly, der, xr);
774 matrix_mult(dy2,coef1,dg,0.0,1.0,'T','N');
775 dy2.reshape(nder,npts);
776
777 for(int ider=0; ider<nder; ++ider) {
778 printf("ider=%d\n",ider);
779 flycoef.copy(coef1);
780 poly_der_to_flypoly(flypoly, flycoef, poly, der, ider, ndeg);
781 evaluate_flypoly_basis(g, flypoly, xr); //this g is really a dg but
782 //only for derivative ider
783 matrix_mult(y,flycoef,g,0.0,1.0,'T','N'); //this y really holds a dy
784 for(int ipt=0; ipt<npts; ++ipt)
785 assert((dy(ider,ipt)==y(0,ipt))&&(dy2(ider,ipt)==y(0,ipt)));
786 int totalderorder=0;
787 for(int ixr=0; ixr<NDV; ++ixr)
788 totalderorder+=der(ixr,ider);
789 for(int ipoly=0; ipoly<npoly; ++ipoly) {
790 int ixr=0;
791 printf("d^%d/dx^[%d",totalderorder,der(ixr,ider));
792 for(ixr=1; ixr<NDV; ++ixr)
793 printf(",%d",der(ixr,ider));
794 ixr=0;
795 printf("](x%d^%d",ixr,poly(ixr,ipoly));
796 for(ixr=1; ixr<NDV; ++ixr)
797 printf("*x%d^%d",ixr,poly(ixr,ipoly));
798 printf(")=%g",flycoef(ipoly,0));
799 int nmult=flypoly(0,ipoly);
800 for(int imult=1; imult<=nmult; ++imult)
801 printf("*x%d",flypoly(imult,ipoly));
802 printf("\n");
803 }
804 printf("\n");
805 }
806 return 0;
807 }
808 #endif
809
810