00001 #include <math.h>
00002 #include <iostream>
00003 #include "chebyshev.h"
00004 #include "tnt_array1d.h"
00005 #include "tnt_array2d.h"
00006 #include "jama_svd.h"
00007 #include "linalg.h"
00008
00009 using namespace TNT;
00010 using namespace JAMA;
00011
00012
00013
00014
00015
00016
00023 double ChebyshevPolynomial(int n, double x){
00024 if (n!=0) {
00025 CChebyshevApprox1D Tk;
00026 Array1D<double> coeffs(n+1);
00027 coeffs=0.0;
00028 coeffs[n]=1.0;
00029 Tk.Setup(1.0,-1.0,n+1,coeffs);
00030 return Tk(x);
00031 } else {return 1.0;}
00032 }
00033
00034
00035
00036
00037
00038
00039
00071 void CChebyshevApprox1D::SetParameters(double upperlimit,double lowerlimit,
00072 int ncoeff)
00073 {
00074
00075 uplim=upperlimit;
00076 lolim=lowerlimit;
00077
00078 nc=ncoeff;
00079 }
00080
00081 void CChebyshevApprox1D::Setup(double upperlimit, double lowerlimit, int ncoeff,
00082 Array1D<double> coeffs)
00083 {
00084
00085 this->SetParameters(upperlimit, lowerlimit, ncoeff);
00086
00087 if (c.dim() != nc) {
00088 Array1D<double> _ctmp(nc);
00089 _ctmp=0.0;
00090 c=_ctmp;
00091 }
00092
00093 if ((coeffs.dim() != nc)&&(!quiet)) cerr << "wrong number of coefficients!\n";
00094 c=coeffs;
00095 };
00096
00114 void CChebyshevApprox1D::Setup(double upperlimit, double lowerlimit,
00115 int ncoeff, double (*func)(double, void*), void *pars)
00116 {
00117
00118 this->SetParameters(upperlimit,lowerlimit,ncoeff);
00119
00120 if (c.dim() != nc) {
00121 Array1D<double> _ctmp(nc);
00122 _ctmp=0.0;
00123 c=_ctmp;
00124 }
00125
00126
00127 double center = 0.5*(uplim+lolim);
00128 double halfrange = 0.5*(uplim-lolim);
00129
00130
00131 Array1D<double> ctmp(nc);
00132 for (int i=0;i<nc;i++){
00133 double y=this->ColocPoint(i);
00134 ctmp[i]=func(y*halfrange+center, pars);
00135 }
00136
00137 double factor=2.0/nc;
00138 for(int i=0;i<nc;i++){
00139 double sum=0.0;
00140 for (int j=0;j<nc;j++)
00141 sum += ctmp[j]*cos(PI*i*(j+0.5)/nc);
00142 c[i] = factor*sum;
00143 }
00144 }
00145
00169 void CChebyshevApprox1D::SetupFit(double upperlimit, double lowerlimit,
00170 int ncoeff, const Array1D<double> &orig_controlpts,
00171 const Array1D<double> &orig_ftnvals)
00172 {
00173
00174 Array1D<double> controlpts,ftnvals;
00175 controlpts=orig_controlpts;
00176 ftnvals=orig_ftnvals;
00177
00178
00179 if ((controlpts.dim() != ftnvals.dim())&&(!quiet)) {
00180 cerr << "Dimensionality of control point and function value" <<
00181 "Vectors are different!\n";
00182 }
00183
00184 if ((controlpts[0]<lowerlimit)||(controlpts[controlpts.dim()-1]>upperlimit))
00185 {
00186 if (!quiet) {
00187 cerr << "Control points don't fit in your fitting interval!!\n";
00188 cerr << "Dammit, i have to do some data pruning\n";
00189 }
00190
00191 int numgoodpts=0;
00192 for (int i=0;i<controlpts.dim();i++){
00193 if ((controlpts[i]>=lowerlimit)&&(controlpts[i]<=upperlimit))
00194 numgoodpts+=1;
00195 }
00196 if (!quiet) {cerr << "Using only "<<numgoodpts<<" points in fit"<<endl;}
00197
00198 Array1D<double> controltmp(numgoodpts);
00199 Array1D<double> ftntmp(numgoodpts);
00200
00201 int igood=0;
00202 for (int i=0;i<controlpts.dim();i++){
00203 if ((controlpts[i]>=lowerlimit)&&(controlpts[i]<=upperlimit)) {
00204 controltmp[igood]=controlpts[i];
00205 ftntmp[igood]=ftnvals[i];
00206 igood+=1;
00207 }
00208 }
00209
00210 controlpts=controltmp;
00211 ftnvals=ftntmp;
00212 }
00213
00214 if ((controlpts.dim() < ncoeff)&&(!quiet)) {
00215 cerr << "You don't have enough control points to constrain the "<<
00216 "requested number of coefficients!\n";
00217 }
00218
00219
00220 int M=controlpts.dim(), N=ncoeff;
00221 Array2D<double> T(M,N);
00222
00223
00224 this->SetParameters(upperlimit,lowerlimit,ncoeff);
00225
00226
00227 if (c.dim() != nc) {
00228 Array1D<double> _ctmp(nc);
00229 _ctmp=0.0;
00230 c=_ctmp;
00231 }
00232
00233
00234 double midpt=0.5*(upperlimit+lowerlimit);
00235 double halfrange=0.5*(upperlimit-lowerlimit);
00236
00237
00238 {
00239 double x;int i,j;
00240 for (i=0;i<M;i++){
00241 x=(controlpts[i]-midpt)/halfrange;
00242 T[i][0]=ChebyshevPolynomial(0,x)-0.5;
00243 for (j=1;j<N;j++){
00244 T[i][j]=ChebyshevPolynomial(j,x);
00245 }
00246 }
00247 }
00248
00249
00250 c=svdinvert(T)*ftnvals;
00251
00252 return;
00253 };
00254
00267 double CChebyshevApprox1D::ColocPoint(int i) const{
00268 return cos(PI*(i+0.5)/nc);
00269 }
00270
00293 double CChebyshevApprox1D::Val(double x, int m) const
00294 {
00295
00296 if ((x>uplim)||(x<lolim)) {
00297 if (!quiet){
00298 cerr << "x="<<x<<" is out of range in CChebyshevApprox1D"<<endl;
00299 cerr << "lolim="<<lolim<<"\n";
00300 cerr << "uplim="<<uplim<<"\n";
00301 }
00302 return 0.0;
00303 }
00304 if ((m<=0)||(m>nc)) {
00305 if (!quiet){
00306 cerr << "m="<<m<<" is an illegal coeff. index in CChebyshevApprox1D"<<endl;
00307 cerr << "nc="<<nc<<"\n";
00308 }
00309 return 0.0;
00310 }
00311
00312 double y,y2,d=0.0,dd=0.0,sv;
00313
00314 y2=2.0*(y=(2.0*x-lolim-uplim)/(uplim-lolim));
00315
00316 for (int j=m-1;j>=1;j--){
00317 sv=d;
00318 d=y2*d-dd+c[j];
00319 dd=sv;
00320 }
00321 return y*d-dd+0.5*c[0];
00322 }
00323
00327 CChebyshevApprox1D CChebyshevApprox1D::Derivative(void) const
00328 {
00329 int j;
00330 double con;
00331 CChebyshevApprox1D deriv;
00332 deriv.SetParameters(uplim,lolim,nc);
00333 {
00334 Array1D<double> _ctmp(deriv.nc);
00335 _ctmp=0.0;
00336 deriv.c=_ctmp;
00337 }
00338
00339
00340 deriv.c[nc-1]=0.0;
00341 deriv.c[nc-2]=2*(nc-1)*c[nc-1];
00342 for (j=nc-3;j>=0;j--) deriv.c[j]=deriv.c[j+2]+2*(j+1)*c[j+1];
00343 con=2.0/(uplim-lolim);
00344 for (j=0;j<nc;j++) deriv.c[j] *= con;
00345
00346 return deriv;
00347 };
00348
00352 CChebyshevApprox1D CChebyshevApprox1D::Integral(void) const
00353 {
00354 int j;
00355 double sum=0.0,fac=1.0,con;
00356 CChebyshevApprox1D integral;
00357 integral.SetParameters(uplim,lolim,nc);
00358 {
00359 Array1D<double> _ctmp(integral.nc);
00360 _ctmp=0.0;
00361 integral.c=_ctmp;
00362 }
00363
00364
00365 con=0.25*(uplim-lolim);
00366 for (j=1;j<=nc-2;j++) {
00367 integral.c[j]=con*(c[j-1]-c[j+1])/j;
00368 sum += fac*integral.c[j];
00369 fac = -fac;
00370 }
00371 integral.c[nc-1]=con*c[nc-2]/(nc-1);
00372 sum += fac*integral.c[nc-1];
00373 integral.c[0]=2.0*sum;
00374
00375
00376 return integral;
00377 };
00378
00436 CChebyshevApprox1D CChebyshevApprox1D::Copy(const CChebyshevApprox1D &A){
00437 if (&A != this) {
00438 uplim=A.uplim;
00439 lolim=A.lolim;
00440 quiet=A.quiet;
00441 nc=A.nc;
00442 c=A.c;
00443 }
00444 return *this;
00445 };
00446
00450 ostream& operator<<(ostream &s, const CChebyshevApprox1D &A)
00451 {
00452 s<<A.uplim<<" "<<A.lolim<<endl<<A.c;
00453 return s;
00454 }
00455
00459 istream& operator>>(istream &s, CChebyshevApprox1D &A)
00460 {
00461 s>>A.uplim>>A.lolim>>A.c;
00462 return s;
00463 }
00464
00491
00492
00493
00494
00495
00496
00497
00502 CChebyshevApprox2D::CChebyshevApprox2D(void)
00503 {
00504 nc_x = 0;
00505 nc_y = 0;
00506 uplim_x = 0.0;
00507 lolim_x = 0.0;
00508 uplim_y = 0.0;
00509 lolim_y = 0.0;
00510 }
00511
00525 CChebyshevApprox2D CChebyshevApprox2D::Copy(const CChebyshevApprox2D &A)
00526 {
00527 if (&A != this) {
00528 uplim_x=A.uplim_x;
00529 lolim_x=A.lolim_x;
00530 uplim_y=A.uplim_y;
00531 lolim_y=A.lolim_y;
00532 nc_x=A.nc_x;
00533 nc_y=A.nc_y;
00534 c=A.c;
00535 }
00536 return *this;
00537 }
00538
00549 void CChebyshevApprox2D::SetParameters(
00550 double upperlimit_x, double lowerlimit_x,
00551 double upperlimit_y, double lowerlimit_y,
00552 int ncoeff_x, const int ncoeff_y)
00553 {
00554 nc_x = ncoeff_x;
00555 nc_y = ncoeff_y;
00556 uplim_x = upperlimit_x;
00557 lolim_x = lowerlimit_x;
00558 uplim_y = upperlimit_y;
00559 lolim_y = lowerlimit_y;
00560 }
00561
00574 double CChebyshevApprox2D::ColocPoint_x(int i) const{
00575 return cos(PI*(i+0.5)/nc_x);
00576 }
00577
00590 double CChebyshevApprox2D::ColocPoint_y(int i) const{
00591 return cos(PI*(i+0.5)/nc_y);
00592 }
00593
00618 void CChebyshevApprox2D::Setup(
00619 double upperlimit_x, double lowerlimit_x,
00620 double upperlimit_y, double lowerlimit_y,
00621 int ncoeff_x, int ncoeff_y,
00622 double (*func)(double,double,void*),void* pars)
00623 {
00624
00625
00626 (*this).SetParameters(
00627 upperlimit_x,lowerlimit_x,
00628 upperlimit_y,lowerlimit_y,
00629 ncoeff_x,ncoeff_y);
00630
00631 if ((c.dim1() != nc_x)||(c.dim2() != nc_y)) {
00632 Array2D<double> _ctmp(nc_x,nc_y);
00633 _ctmp=0.0;
00634 c=_ctmp;
00635 }
00636
00637
00638 double center_x = 0.5*(uplim_x+lolim_x);
00639 double halfrange_x = 0.5*(uplim_x-lolim_x);
00640 double center_y = 0.5*(uplim_y+lolim_y);
00641 double halfrange_y = 0.5*(uplim_y-lolim_y);
00642
00643 Array2D<double> ctmp(nc_x,nc_y);
00644 for (int i=0;i<nc_x;i++){
00645 for (int j=0;j<nc_y;j++){
00646 ctmp[i][j]=func(this->ColocPoint_x(i)*halfrange_x+center_x,
00647 this->ColocPoint_y(j)*halfrange_y+center_y,pars);
00648 }
00649 }
00650
00651 double factor=4.0/nc_x/nc_y;
00652 for (int i=0;i<nc_x;i++){
00653 for (int j=0;j<nc_y;j++){
00654 double sum=0.0;
00655 for (int k=0;k<nc_x;k++){
00656 for (int l=0;l<nc_y;l++){
00657 sum+=ctmp[k][l]*cos(PI*i*(k+0.5)/nc_x)*cos(PI*j*(l+0.5)/nc_y);
00658 }
00659 }
00660 c[i][j]=factor*sum;
00661 }
00662 }
00663
00664 }
00665
00698 void CChebyshevApprox2D::SetupFit(double upperlimit_x, double lowerlimit_x,
00699 double upperlimit_y, double lowerlimit_y, int ncoeff_x, int ncoeff_y,
00700 const Array1D<double> &x_controlpts, const Array1D<double> &y_controlpts,
00701 const Array2D<double> &ftnvals)
00702 {
00703
00704
00705 if (x_controlpts.dim() != ftnvals.dim1()) cerr <<
00706 "Dimensionality of x control point and function value " <<
00707 "Vectors are different!\n";
00708 if (y_controlpts.dim() != ftnvals.dim2()) cerr <<
00709 "Dimensionality of y control point and function value " <<
00710 "Vectors are different!\n";
00711 if (x_controlpts.dim() < ncoeff_x) cerr <<
00712 "You don't have enough x control points to constrain the "<<
00713 "requested number of coefficients!\n";
00714 if (y_controlpts.dim() < ncoeff_y) cerr <<
00715 "You don't have enough y control points to constrain the "<<
00716 "requested number of coefficients!\n";
00717
00718 if ((x_controlpts[0]<lowerlimit_x)||
00719 (x_controlpts[x_controlpts.dim()-1]>upperlimit_x))
00720 cerr << "x Control points don't fit in your fitting interval!!\n";
00721 if ((y_controlpts[0]<lowerlimit_y)||
00722 (y_controlpts[y_controlpts.dim()-1]>upperlimit_y))
00723 cerr << "y Control points don't fit in your fitting interval!!\n";
00724
00725
00726 (*this).SetParameters(
00727 upperlimit_x,lowerlimit_x,
00728 upperlimit_y,lowerlimit_y,
00729 ncoeff_x,ncoeff_y);
00730
00731 if ((c.dim1() != nc_x)||(c.dim2() != nc_y)) {
00732 Array2D<double> _ctmp(nc_x,nc_y);
00733 _ctmp=0.0;
00734 c=_ctmp;
00735 }
00736
00737
00738
00739 double center_x = 0.5*(uplim_x+lolim_x);
00740 double halfrange_x = 0.5*(uplim_x-lolim_x);
00741 double center_y = 0.5*(uplim_y+lolim_y);
00742 double halfrange_y = 0.5*(uplim_y-lolim_y);
00743
00744
00745 clog << " Building fit matrix\n";
00746
00747 int Mpts=x_controlpts.dim()*y_controlpts.dim(), Ncoeffs=ncoeff_x*ncoeff_y;
00748 Array2D<double> T(Mpts,Ncoeffs);
00749 {
00750 double x,y;
00751 double xcoeff,ycoeff;
00752 int ipt=0;
00753 for (int i=0;i<x_controlpts.dim();i++){
00754 x=(x_controlpts[i]-center_x)/halfrange_x;
00755 for (int j=0;j<y_controlpts.dim();j++){
00756 y=(y_controlpts[j]-center_y)/halfrange_y;
00757 int icoeff=0;
00758 for (int k=0;k<ncoeff_x;k++){
00759 if ( k == 0 ) {xcoeff=ChebyshevPolynomial(0,x)-0.5;}
00760 else {xcoeff=ChebyshevPolynomial(k,x);}
00761 for (int l=0;l<ncoeff_y;l++){
00762 if ( l == 0 ) {ycoeff=ChebyshevPolynomial(0,y)-0.5;}
00763 else {ycoeff=ChebyshevPolynomial(l,y);}
00764 T[ipt][icoeff]=xcoeff*ycoeff;
00765 icoeff+=1;
00766 }
00767 }
00768 ipt+=1;
00769 }
00770 }
00771 }
00772
00773 clog << " Inverting to find Matrix of coefficients\n";
00774
00775 Array1D<double> ftnvalstmp(Mpts);
00776 {
00777 int ipt=0;
00778 for (int i=0;i<x_controlpts.dim();i++){
00779 for (int j=0;j<y_controlpts.dim();j++){
00780 ftnvalstmp[ipt]=ftnvals[i][j];
00781 ipt+=1;
00782 }
00783 }
00784 }
00785
00786
00787 Array1D<double> ctmp(Ncoeffs);
00788 ctmp=svdinvert(T)*ftnvalstmp;
00789
00790
00791 {
00792 int icoeff=0;
00793 for (int k=0;k<ncoeff_x;k++){
00794 for (int l=0;l<ncoeff_y;l++){
00795 c[k][l]=ctmp[icoeff];
00796 icoeff+=1;
00797 }
00798 }
00799 }
00800 clog << "Coefficients for Chebyshev Approx. computed!\n";
00801 }
00802
00843 double CChebyshevApprox2D::Val(double x, double y, int m_x, int m_y) const
00844 {
00845
00846 if ((x > uplim_x) || (x < lolim_x)) cout << "x argument outside of region" <<
00847 " of validity of Chebyshev approximation. " << endl;
00848 if ((y > uplim_y) || (y < lolim_y)) cout << "y argument outside of region" <<
00849 " of validity of Chebyshev approximation. " << endl;
00850 if ((m_x > nc_x) || (m_x < 1)) cout << "illegal m_x requested" << endl;
00851 if ((m_y > nc_y) || (m_y < 1)) cout << "illegal m_y requested" << endl;
00852
00853
00854 double xt2,xt,yt2,yt,d,dd,sv;
00855
00856 xt2=2.0*(xt=(2.0*x-lolim_x-uplim_x)/(uplim_x-lolim_x));
00857 yt2=2.0*(yt=(2.0*y-lolim_y-uplim_y)/(uplim_y-lolim_y));
00858
00859
00860 Array1D<double> ctmp(nc_x);
00861
00862
00863 for (int i=0;i<nc_x;i++){
00864 d=dd=0.0;
00865 for (int j=m_y-1;j>=1;j--){
00866 sv=d;
00867 d=yt2*d-dd+c[i][j];
00868 dd=sv;
00869 }
00870 ctmp[i]=yt*d-dd+0.5*c[i][0];
00871 }
00872
00873 d=dd=0.0;
00874 for (int i=m_x-1;i>=1;i--){
00875 sv=d;
00876 d=xt2*d-dd+ctmp[i];
00877 dd=sv;
00878 }
00879 return xt*d-dd+0.5*ctmp[0];
00880 }
00881
00885 ostream& operator<<(ostream &s, const CChebyshevApprox2D &A)
00886 {
00887 s<<A.uplim_x<<" "<<A.lolim_x<<endl;
00888 s<<A.uplim_y<<" "<<A.lolim_y<<endl;
00889 s<<A.c;
00890 return s;
00891 }
00892
00896 istream& operator>>(istream &s, CChebyshevApprox2D &A)
00897 {
00898 s>>A.uplim_x>>A.lolim_x;
00899 s>>A.uplim_y>>A.lolim_y;
00900 s>>A.c;
00901 return s;
00902 }
00903