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