File indexing completed on 2025-02-23 09:21:02
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040 #include <iostream>
0041 #include <vector>
0042 #include <algorithm>
0043 #include <cstdlib>
0044 #include <numeric>
0045 #include <functional>
0046 #include <iterator>
0047 #include <cmath>
0048 #include <limits>
0049 #include <sstream>
0050
0051 template <typename float_type> const std::string
0052 c2_function<float_type>::cvs_file_vers() const
0053 { return "c2_function.cc 488 2012-04-04 11:30:21Z marcus "; }
0054
0055 template <typename float_type> float_type
0056 c2_function<float_type>::find_root(float_type lower_bracket,
0057 float_type upper_bracket,
0058 float_type start, float_type value, int *error,
0059 float_type *final_yprime, float_type *final_yprime2) const
0060
0061 {
0062
0063
0064
0065
0066
0067 float_type yp, yp2;
0068 if (!final_yprime) final_yprime=&yp;
0069 if (!final_yprime2) final_yprime2=&yp2;
0070
0071 float_type ftol=5*(std::numeric_limits<float_type>::epsilon()
0072 *std::abs(value)+std::numeric_limits<float_type>::min());
0073 float_type xtol=5*(std::numeric_limits<float_type>::epsilon()
0074 *(std::abs(upper_bracket)+std::abs(lower_bracket))
0075 +std::numeric_limits<float_type>::min());
0076
0077 float_type root=start;
0078 if(error) *error=0;
0079
0080 float_type c, b;
0081 if(!root_info) {
0082 root_info=new struct c2_root_info;
0083 root_info->inited=false;
0084 }
0085
0086
0087
0088
0089
0090
0091 if(!root_info->inited || upper_bracket != root_info->upper.x
0092 || lower_bracket != root_info->lower.x) {
0093 root_info->upper.x=upper_bracket;
0094 fill_fblock(root_info->upper);
0095 root_info->lower.x=lower_bracket;
0096 fill_fblock(root_info->lower);
0097 root_info->inited=true;
0098 }
0099
0100 float_type clower=root_info->lower.y-value;
0101 if(!clower) {
0102 *final_yprime=root_info->lower.yp;
0103 *final_yprime2=root_info->lower.ypp;
0104 return lower_bracket;
0105 }
0106
0107 float_type cupper=root_info->upper.y-value;
0108 if(!cupper) {
0109 *final_yprime=root_info->upper.yp;
0110 *final_yprime2=root_info->upper.ypp;
0111 return upper_bracket;
0112 }
0113 const float_type lower_sign = (clower < 0) ? -1 : 1;
0114
0115 if(lower_sign*cupper >0) {
0116
0117 if(error) { *error=1; return 0.0; }
0118 else {
0119 std::ostringstream outstr;
0120 outstr << "unbracketed root in find_root at xlower= " << lower_bracket
0121 << ", xupper= " << upper_bracket;
0122 outstr << ", value= " << value << ": bailing";
0123 throw c2_exception(outstr.str().c_str());
0124 }
0125 }
0126
0127 float_type delta=upper_bracket-lower_bracket;
0128 c=value_with_derivatives(root, final_yprime, final_yprime2)-value;
0129 b=*final_yprime;
0130 increment_evaluations();
0131
0132 while(
0133 std::abs(delta) > xtol &&
0134 std::abs(c) > ftol &&
0135 std::abs(c) > xtol*std::abs(b)
0136 )
0137 {
0138 float_type a=(*final_yprime2)/2;
0139 float_type disc=b*b-4*a*c;
0140
0141
0142
0143 if(disc >= 0) {
0144 float_type q=-0.5*((b>=0)?(b+std::sqrt(disc)):(b-std::sqrt(disc)));
0145 if(q*q > std::abs(a*c)) delta=c/q;
0146
0147 else delta=q/a;
0148 root+=delta;
0149 }
0150
0151 if(disc < 0 || root<lower_bracket || root>upper_bracket ||
0152 std::abs(delta) >= 0.5*(upper_bracket-lower_bracket)) {
0153
0154 root=0.5*(lower_bracket+upper_bracket);
0155 delta=upper_bracket-lower_bracket;
0156 }
0157 c=value_with_derivatives(root, final_yprime, final_yprime2)-value;
0158
0159 if(c2_isnan(c)) {
0160 bad_x_point=root;
0161 return c;
0162 }
0163 b=*final_yprime;
0164 increment_evaluations();
0165
0166
0167 if(c*lower_sign < 0.0) {
0168 cupper=c;
0169 upper_bracket=root;
0170 } else {
0171 clower=c;
0172 lower_bracket=root;
0173 }
0174
0175
0176 }
0177 return root;
0178 }
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197 template <typename float_type> float_type
0198 c2_function<float_type>::integrate_step(c2_integrate_recur &rb) const
0199
0200 {
0201 std::vector< recur_item > &rb_stack=*rb.rb_stack;
0202
0203 rb_stack.clear();
0204
0205 recur_item top;
0206 top.depth=0; top.done=false; top.f0index=0; top.f2index=0; top.step_sum=0;
0207
0208
0209 rb_stack.push_back(top);
0210 rb_stack.back().f1=*rb.f0;
0211 rb_stack.back().done=true;
0212
0213 rb_stack.push_back(top);
0214 rb_stack.back().f1=*rb.f1;
0215 rb_stack.back().done=true;
0216
0217 if(!rb.inited) {
0218 switch(rb.derivs) {
0219 case 0:
0220 rb.eps_scale=0.1; rb.extrap_coef=16; break;
0221 case 1:
0222 rb.eps_scale=0.1; rb.extrap_coef=64; break;
0223 case 2:
0224 rb.eps_scale=0.02; rb.extrap_coef=1024; break;
0225 default:
0226 throw c2_exception("derivs must be 0, 1 or 2 in partial_integrals");
0227 }
0228
0229 rb.extrap2=1.0/(rb.extrap_coef-1.0);
0230 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
0231 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
0232 rb.inited=true;
0233 }
0234
0235
0236 top.f0index=0;
0237 top.f2index=1;
0238 top.abs_tol=rb.abs_tol;
0239 rb_stack.push_back(top);
0240
0241 while(rb_stack.size() > 2) {
0242 recur_item &back=rb_stack.back();
0243 if(back.done) {
0244 float_type sum=back.step_sum;
0245 rb_stack.pop_back();
0246 rb_stack.back().step_sum+=sum;
0247 continue;
0248 }
0249 back.done=true;
0250
0251 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1,
0252 &f2=rb_stack[back.f2index].f1;
0253 c2_fblock<float_type> &f1=back.f1;
0254 size_t f1index=rb_stack.size()-1;
0255 float_type abs_tol=back.abs_tol;
0256
0257 f1.x=0.5*(f0.x + f2.x);
0258 float_type dx2=0.5*(f2.x - f0.x);
0259
0260
0261 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance
0262 || std::abs(dx2) < rb.abs_tol_min) {
0263 std::ostringstream outstr;
0264 outstr << "Step size underflow in adaptive_partial_integrals at depth="
0265 << back.depth << ", x= " << f1.x;
0266 throw c2_exception(outstr.str().c_str());
0267 }
0268
0269 fill_fblock(f1);
0270 if(c2_isnan(f1.y)) {
0271 bad_x_point=f1.x;
0272 return f1.y;
0273 }
0274
0275 bool yptrouble=f0.ypbad || f2.ypbad || f1.ypbad;
0276 bool ypptrouble=f0.yppbad || f2.yppbad || f1.yppbad;
0277
0278
0279
0280 int derivs = std::min(rb.derivs, (yptrouble||ypptrouble)?(yptrouble?0:1):2);
0281
0282 if(!back.depth) {
0283 switch(derivs) {
0284 case 0:
0285 back.previous_estimate=(f0.y+f2.y)*dx2; break;
0286 case 1:
0287 back.previous_estimate=(f0.y+4.0*f1.y+f2.y)*dx2/3.0; break;
0288 case 2:
0289 back.previous_estimate=( (14*f0.y + 32*f1.y + 14*f2.y)
0290 + 2*dx2 * (f0.yp - f2.yp) ) * dx2 /30.; break;
0291 default:
0292 back.previous_estimate=0.0;
0293 }
0294 }
0295
0296 float_type left, right;
0297
0298
0299
0300
0301
0302 static const float_type c0c1=5./((float_type)12.),
0303 c0c2=8./((float_type)12.), c0c3=-1./((float_type)12.);
0304
0305 static const float_type c1c1=101./((float_type)240.),
0306 c1c2=128./((float_type)240.), c1c3=11./((float_type)240.),
0307 c1c4=13./((float_type)240.), c1c5=-40./((float_type)240.),
0308 c1c6=-3./((float_type)240.);
0309
0310 static const float_type c2c1=169./((float_type)40320.),
0311 c2c2=1024./ ((float_type)40320.), c2c3=-41./((float_type)40320.),
0312 c2c4=2727./((float_type)40320.), c2c5=-5040./((float_type)40320.),
0313 c2c6=423./((float_type)40320.),
0314 c2c7=17007./((float_type)40320.), c2c8=24576./((float_type)40320.),
0315 c2c9=-1263./((float_type)40320.);
0316
0317 switch(derivs) {
0318 case 2:
0319
0320 left=( ( (c2c1*f0.ypp + c2c2*f1.ypp + c2c3*f2.ypp)*dx2 +
0321 (c2c4*f0.yp + c2c5*f1.yp + c2c6*f2.yp) )*dx2 +
0322 (c2c7*f0.y + c2c8*f1.y + c2c9*f2.y) )* dx2;
0323 right=( ( (c2c1*f2.ypp + c2c2*f1.ypp + c2c3*f0.ypp)*dx2 -
0324 (c2c4*f2.yp + c2c5*f1.yp + c2c6*f0.yp) )*dx2 +
0325 (c2c7*f2.y + c2c8*f1.y + c2c9*f0.y) )* dx2;
0326
0327
0328
0329 break;
0330 case 1:
0331 left= ( (c1c1*f0.y + c1c2*f1.y + c1c3*f2.y)
0332 + dx2*(c1c4*f0.yp + c1c5*f1.yp + c1c6*f2.yp) ) * dx2 ;
0333 right= ( (c1c1*f2.y + c1c2*f1.y + c1c3*f0.y)
0334 - dx2*(c1c4*f2.yp + c1c5*f1.yp + c1c6*f0.yp) ) * dx2 ;
0335 break;
0336 case 0:
0337 left=(c0c1*f0.y + c0c2*f1.y + c0c3*f2.y)*dx2;
0338 right=(c0c1*f2.y + c0c2*f1.y + c0c3*f0.y)*dx2;
0339 break;
0340 default:
0341 left=right=0.0;
0342 break;
0343 }
0344
0345 float_type lrsum=left+right;
0346
0347 bool extrapolate=back.depth && rb.extrapolate && (derivs==rb.derivs);
0348
0349 float_type eps=std::abs(back.previous_estimate-lrsum)*rb.eps_scale;
0350 if(extrapolate) eps*=rb.eps_scale;
0351
0352 if(rb.adapt && eps > abs_tol && eps > std::abs(lrsum)*rb.rel_tol) {
0353
0354 if(abs_tol > rb.abs_tol_min) abs_tol=abs_tol*0.5;
0355
0356 top.abs_tol=abs_tol;
0357 top.depth=back.depth+1;
0358
0359
0360
0361 size_t f0index=back.f0index, f2index=back.f2index;
0362
0363 top.f0index=f1index; top.f2index=f2index;
0364
0365 top.previous_estimate=right;
0366 rb_stack.push_back(top);
0367
0368 top.f0index=f0index; top.f2index=f1index;
0369
0370 top.previous_estimate=left;
0371 rb_stack.push_back(top);
0372
0373 } else if(extrapolate) {
0374
0375 back.step_sum+=(rb.extrap_coef*lrsum - back.previous_estimate)*rb.extrap2;
0376 } else {
0377 back.step_sum+=lrsum;
0378 }
0379 }
0380 return rb_stack.back().step_sum;
0381 }
0382
0383 template <typename float_type> bool c2_function<float_type>::check_monotonicity(
0384 const std::vector<float_type> &data,
0385 const char message[]) const
0386 {
0387 size_t np=data.size();
0388 if(np < 2) return false;
0389
0390 bool rev=(data[1] < data[0]);
0391 size_t i;
0392
0393 if(!rev) for(i = 2; i < np && (data[i-1] < data[i]) ; i++) { }
0394 else for(i = 2; i < np &&(data[i-1] > data[i]) ; i++) { }
0395
0396 if(i != np) throw c2_exception(message);
0397
0398 return rev;
0399 }
0400
0401 template <typename float_type> void
0402 c2_function<float_type>::set_sampling_grid(const std::vector<float_type> &grid)
0403
0404 {
0405 bool rev=this->check_monotonicity(grid,
0406 "set_sampling_grid: sampling grid not monotonic");
0407
0408 if(!sampling_grid || no_overwrite_grid) sampling_grid=
0409 new std::vector<float_type>;
0410 (*sampling_grid)=grid; no_overwrite_grid=0;
0411
0412 if(rev) std::reverse(sampling_grid->begin(), sampling_grid->end());
0413
0414 }
0415
0416 template <typename float_type> void c2_function<float_type>::
0417 get_sampling_grid(float_type amin, float_type amax,
0418 std::vector<float_type> &grid) const
0419 {
0420 std::vector<float_type> *result=&grid;
0421 result->clear();
0422
0423 if( !(sampling_grid) || !(sampling_grid->size())
0424 || (amax <= sampling_grid->front()) || (amin >= sampling_grid->back()) ) {
0425
0426 result->push_back(amin);
0427 result->push_back(amax);
0428 } else {
0429 std::vector<float_type> &sg=*sampling_grid;
0430 int np=sg.size();
0431 int klo=0, khi=np-1, firstindex=0, lastindex=np-1;
0432
0433 result->push_back(amin);
0434
0435 if(amin > sg.front() ) {
0436
0437 while(khi-klo > 1) {
0438 int kk=(khi+klo)/2;
0439 if(sg[kk] > amin) khi=kk;
0440 else klo=kk;
0441 }
0442 khi=klo+1;
0443
0444
0445 firstindex=khi;
0446 khi=np-1;
0447 }
0448
0449 if(amax < sg.back()) {
0450
0451 while(khi-klo > 1) {
0452 int kk=(khi+klo)/2;
0453 if(sg[kk] > amax) khi=kk;
0454 else klo=kk;
0455 }
0456 khi=klo+1;
0457
0458
0459 lastindex=klo;
0460 }
0461
0462 int initsize=result->size();
0463 result->resize(initsize+(lastindex-firstindex+2));
0464 std::copy(sg.begin()+firstindex, sg.begin()+lastindex+1,
0465 result->begin()+initsize);
0466 result->back()=amax;
0467
0468
0469
0470 preen_sampling_grid(result);
0471 }
0472 }
0473
0474 template <typename float_type> void
0475 c2_function<float_type>::preen_sampling_grid(std::vector<float_type> *result)
0476 const
0477 {
0478
0479
0480 if(result->size() > 2) {
0481
0482
0483 bool deleteit=false;
0484 float_type x0=(*result)[0], x1=(*result)[1];
0485 float_type dx1=x1-x0;
0486
0487 float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()
0488 *(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
0489 if(dx1 < ftol) deleteit=true;
0490 float_type dx2=(*result)[2]-x0;
0491 if(dx1/dx2 < 0.1) deleteit=true;
0492
0493
0494 if(deleteit) result->erase(result->begin()+1);
0495
0496 }
0497
0498 if(result->size() > 2) {
0499
0500
0501 bool deleteit=false;
0502 int pos=result->size()-3;
0503 float_type x0=(*result)[pos+1], x1=(*result)[pos+2];
0504 float_type dx1=x1-x0;
0505
0506 float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()
0507 *(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
0508 if(dx1 < ftol) deleteit=true;
0509 float_type dx2=x1-(*result)[pos];
0510 if(dx1/dx2 < 0.1) deleteit=true;
0511
0512
0513 if(deleteit) result->erase(result->end()-2);
0514
0515 }
0516 }
0517
0518 template <typename float_type> void c2_function<float_type>::
0519 refine_sampling_grid(std::vector<float_type> &grid, size_t refinement) const
0520 {
0521 size_t np=grid.size();
0522 size_t count=(np-1)*refinement + 1;
0523 float_type dxscale=1.0/refinement;
0524
0525 std::vector<float_type> result(count);
0526
0527 for(size_t i=0; i<(np-1); i++) {
0528 float_type x=grid[i];
0529 float_type dx=(grid[i+1]-x)*dxscale;
0530 for(size_t j=0; j<refinement; j++, x+=dx) result[i*refinement+j]=x;
0531 }
0532 result.back()=grid.back();
0533 grid=result;
0534 }
0535
0536 template <typename float_type> float_type
0537 c2_function<float_type>::integral(float_type amin, float_type amax,
0538 std::vector<float_type> *partials,
0539 float_type abs_tol, float_type rel_tol, int derivs,
0540 bool adapt, bool extrapolate) const
0541 {
0542 if(amin==amax) {
0543 if(partials) partials->clear();
0544 return 0.0;
0545 }
0546 std::vector<float_type> grid;
0547 get_sampling_grid(amin, amax, grid);
0548 float_type intg=partial_integrals(grid, partials, abs_tol, rel_tol,
0549 derivs, adapt, extrapolate);
0550 return intg;
0551 }
0552
0553 template <typename float_type> c2_function<float_type>
0554 &c2_function<float_type>::normalized_function(float_type amin,
0555 float_type amax, float_type norm)
0556 const
0557 {
0558 float_type intg=integral(amin, amax);
0559 return *new c2_scaled_function_p<float_type>(*this, norm/intg);
0560 }
0561
0562 template <typename float_type> c2_function<float_type>
0563 &c2_function<float_type>::square_normalized_function(float_type amin,
0564 float_type amax, float_type norm)
0565 const
0566 {
0567 c2_ptr<float_type> mesquared((*new
0568 c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this));
0569
0570 std::vector<float_type> grid;
0571 get_sampling_grid(amin, amax, grid);
0572 float_type intg=mesquared->partial_integrals(grid);
0573
0574 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
0575 }
0576
0577 template <typename float_type> c2_function<float_type>
0578 &c2_function<float_type>::square_normalized_function(
0579 float_type amin, float_type amax, const c2_function<float_type> &weight,
0580 float_type norm)
0581 const
0582 {
0583 c2_ptr<float_type> weighted((*new
0584 c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this) * weight);
0585
0586 std::vector<float_type> grid;
0587 get_sampling_grid(amin, amax, grid);
0588 float_type intg=weighted->partial_integrals(grid);
0589
0590 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
0591 }
0592
0593 template <typename float_type> float_type
0594 c2_function<float_type>::partial_integrals(
0595 std::vector<float_type> xgrid, std::vector<float_type> *partials,
0596 float_type abs_tol, float_type rel_tol, int derivs,
0597 bool adapt, bool extrapolate)
0598 const
0599 {
0600 int np=xgrid.size();
0601
0602 c2_fblock<float_type> f0, f2;
0603 struct c2_integrate_recur rb;
0604 rb.rel_tol=rel_tol;
0605 rb.extrapolate=extrapolate;
0606 rb.adapt=adapt;
0607 rb.derivs=derivs;
0608 std::vector< recur_item > rb_stack;
0609 rb_stack.reserve(20);
0610 rb.rb_stack=&rb_stack;
0611 rb.inited=false;
0612 float_type dx_inv=1.0/std::abs(xgrid.back()-xgrid.front());
0613
0614 if(partials) partials->resize(np-1);
0615
0616 float_type sum=0.0;
0617
0618 f2.x=xgrid[0];
0619 fill_fblock(f2);
0620 if(c2_isnan(f2.y)) {
0621 bad_x_point=f2.x;
0622 return f2.y;
0623 }
0624
0625 for(int i=0; i<np-1; i++) {
0626 f0=f2;
0627
0628 f2.x=xgrid[i+1];
0629 fill_fblock(f2);
0630 if(c2_isnan(f2.y)) {
0631 bad_x_point=f2.x;
0632 return f2.y;
0633 }
0634
0635 rb.abs_tol=abs_tol*std::abs(f2.x-f0.x)*dx_inv;
0636
0637 rb.f0=&f0; rb.f1=&f2;
0638 float_type psss=integrate_step(rb);
0639 sum+=psss;
0640 if(partials) (*partials)[i]=psss;
0641 if(c2_isnan(psss)) break;
0642 }
0643 return sum;
0644 }
0645
0646
0647
0648 template <typename float_type> void c2_sin_p<float_type>::
0649 get_sampling_grid(float_type amin, float_type amax,
0650 std::vector<float_type> &grid) const
0651 {
0652 grid.clear();
0653 for(; amin < amax; amin+=5.0) grid.push_back(amin);
0654 grid.push_back(amax);
0655 this->preen_sampling_grid(&grid);
0656 }
0657
0658 template <typename float_type> float_type
0659 c2_function_transformation<float_type>::evaluate(
0660 float_type xraw,
0661 float_type y, float_type yp0, float_type ypp0,
0662 float_type *yprime, float_type *yprime2) const
0663 {
0664 y=Y.fHasStaticTransforms ? Y.pOut(y) : Y.fOut(y);
0665
0666 if(yprime || yprime2) {
0667
0668 float_type yp, yp2;
0669 if(X.fHasStaticTransforms && Y.fHasStaticTransforms) {
0670 float_type fpi=1.0/Y.pInPrime(y);
0671 float_type gp=X.pInPrime(xraw);
0672
0673 yp=gp*yp0*fpi;
0674 yp2=(gp*gp*ypp0 + X.pInDPrime(xraw)*yp0 - Y.pInDPrime(y)*yp*yp )*fpi;
0675 } else {
0676 float_type fpi=1.0/Y.fInPrime(y);
0677 float_type gp=X.fInPrime(xraw);
0678
0679 yp=gp*yp0*fpi;
0680 yp2=(gp*gp*ypp0 + X.fInDPrime(xraw)*yp0 - Y.fInDPrime(y)*yp*yp )*fpi;
0681 }
0682 if(yprime) *yprime=yp;
0683 if(yprime2) *yprime2=yp2;
0684 }
0685 return y;
0686 }
0687
0688
0689 template <typename float_type> interpolating_function_p<float_type>
0690 &interpolating_function_p<float_type>::load(
0691 const std::vector<float_type> &x, const std::vector<float_type> &f,
0692 bool lowerSlopeNatural, float_type lowerSlope,
0693 bool upperSlopeNatural, float_type upperSlope,
0694 bool splined
0695 )
0696 {
0697 c2_ptr<float_type> keepme(*this);
0698 X= x;
0699 F= f;
0700
0701 Xraw=x;
0702
0703 this->set_domain(std::min(Xraw.front(), Xraw.back()),
0704 std::max(Xraw.front(), Xraw.back()));
0705
0706 if(x.size() != f.size()) {
0707 throw c2_exception(
0708 "interpolating_function::init() -- x & y inputs are of different size");
0709 }
0710
0711 size_t np=X.size();
0712
0713 if(np < 2) {
0714 throw c2_exception("interpolating_function::init() -- input < 2 elements ");
0715 }
0716
0717 bool xraw_rev=this->check_monotonicity(Xraw,
0718 "interpolating_function::init() non-monotonic raw x input");
0719
0720
0721 if(!xraw_rev) {
0722
0723 this->set_sampling_grid_pointer(Xraw);
0724
0725 } else {
0726 this->set_sampling_grid(Xraw);
0727
0728 }
0729
0730 if(fTransform.X.fTransformed) {
0731
0732 if(!lowerSlopeNatural) lowerSlope /= fTransform.X.fInPrime(X[0]);
0733 if(!upperSlopeNatural) upperSlope /= fTransform.X.fInPrime(X[np-1]);
0734 for(size_t i=0; i<np; i++) X[i]=fTransform.X.fIn(X[i]);
0735 }
0736 if(fTransform.Y.fTransformed) {
0737
0738 if(!lowerSlopeNatural) lowerSlope *= fTransform.Y.fInPrime(F[0]);
0739 if(!upperSlopeNatural) upperSlope *= fTransform.Y.fInPrime(F[np-1]);
0740 for(size_t i=0; i<np; i++) F[i]=fTransform.Y.fIn(F[i]);
0741 }
0742
0743 xInverted=this->check_monotonicity(X,
0744 "interpolating_function::init() non-monotonic transformed x input");
0745
0746 if(splined) spline(lowerSlopeNatural, lowerSlope,
0747 upperSlopeNatural, upperSlope);
0748 else y2.assign(np,0.0);
0749
0750 lastKLow=0;
0751 keepme.release_for_return();
0752 return *this;
0753 }
0754
0755
0756
0757 template <typename float_type> interpolating_function_p<float_type>
0758 &interpolating_function_p<float_type>::load_pairs(
0759 std::vector<std::pair<float_type, float_type> > &data,
0760 bool lowerSlopeNatural, float_type lowerSlope,
0761 bool upperSlopeNatural, float_type upperSlope,
0762 bool splined
0763 )
0764 {
0765 c2_ptr<float_type> keepme(*this);
0766
0767 size_t np=data.size();
0768 if(np < 2) {
0769 throw c2_exception("interpolating_function::init() -- input < 2 elements ");
0770 }
0771
0772
0773 std::sort(data.begin(), data.end(), comp_pair);
0774
0775 std::vector<float_type> xtmp, ytmp;
0776 xtmp.reserve(np);
0777 ytmp.reserve(np);
0778 for (size_t i=0; i<np; i++) {
0779 xtmp.push_back(data[i].first);
0780 ytmp.push_back(data[i].second);
0781 }
0782 this->load(xtmp, ytmp, lowerSlopeNatural, lowerSlope,
0783 upperSlopeNatural, upperSlope, splined);
0784
0785 keepme.release_for_return();
0786 return *this;
0787 }
0788
0789 template <typename float_type> interpolating_function_p<float_type> &
0790 interpolating_function_p<float_type>::load_random_generator_function(
0791 const std::vector<float_type> &bincenters,
0792 const c2_function<float_type> &binheights)
0793
0794 {
0795 c2_ptr<float_type> keepme(*this);
0796
0797 std::vector<float_type> integral;
0798 c2_const_ptr<float_type> keepit(binheights);
0799
0800
0801
0802
0803 float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6);
0804 integral.insert(integral.begin(), 0.0);
0805 float_type scale=1.0/sum;
0806 for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale
0807 + integral[i-1];
0808 integral.back()=1.0;
0809
0810 this->load(integral, bincenters,
0811 false, 1.0/(scale*binheights(bincenters.front() )),
0812 false, 1.0/(scale*binheights(bincenters.back() ))
0813 );
0814 keepme.release_for_return();
0815 return *this;
0816 }
0817
0818 template <typename float_type> interpolating_function_p<float_type> &
0819 interpolating_function_p<float_type>::load_random_generator_bins(
0820 const std::vector<float_type> &bins,
0821 const std::vector<float_type> &binheights, bool splined)
0822
0823 {
0824 c2_ptr<float_type> keepme(*this);
0825
0826 size_t np=binheights.size();
0827 std::vector<float_type> integral(np+1), bin_edges(np+1);
0828
0829
0830
0831
0832
0833
0834
0835 if(bins.size() == binheights.size()+1) {
0836 bin_edges=bins;
0837 } else if (bins.size() == binheights.size()) {
0838 bin_edges.front()=bins[0] - (bins[1]-bins[0])*0.5;
0839 for(size_t i=1; i<np; i++) {
0840 bin_edges[i]=(bins[i]+bins[i-1])*0.5;
0841 }
0842 bin_edges.back()=bins[np-1] + (bins[np-1]-bins[np-2])*0.5;
0843 } else {
0844 throw c2_exception(
0845 "inconsistent bin vectors passed to load_random_generator_bins");
0846 }
0847
0848 float_type running_sum=0.0;
0849 for(size_t i=0; i<np; i++) {
0850 integral[i]=running_sum;
0851 if(!binheights[i]) throw c2_exception(
0852 "empty bin passed to load_random_generator_bins");
0853 running_sum+=binheights[i]*std::abs(bin_edges[i+1]-bin_edges[i]);
0854 }
0855 float_type scale=1.0/running_sum;
0856 for(size_t i=0; i<np; i++) integral[i]*=scale;
0857 integral.back()=1.0;
0858 this->load(integral, bin_edges,
0859 false, 1.0/(scale*binheights.front()),
0860 false, 1.0/(scale*binheights.back()),
0861 splined);
0862 keepme.release_for_return();
0863 return *this;
0864 }
0865
0866
0867
0868 template <typename float_type> void
0869 interpolating_function_p<float_type>::spline(
0870 bool lowerSlopeNatural, float_type lowerSlope,
0871 bool upperSlopeNatural, float_type upperSlope
0872 )
0873 {
0874
0875
0876
0877 size_t np=X.size();
0878 std::vector<float_type> u(np), dy(np-1), dx(np-1),
0879 dxi(np-1), dx2i(np-2), siga(np-2), dydx(np-1);
0880
0881 std::transform(X.begin()+1, X.end(), X.begin(), dx.begin(),
0882 std::minus<float_type>() );
0883 for(size_t i=0; i<dxi.size(); i++) dxi[i]=1.0/dx[i];
0884 for(size_t i=0; i<dx2i.size(); i++) dx2i[i]=1.0/(X[i+2]-X[i]);
0885
0886 std::transform(F.begin()+1, F.end(), F.begin(), dy.begin(),
0887 std::minus<float_type>() );
0888 std::transform(dx2i.begin(), dx2i.end(), dx.begin(), siga.begin(),
0889 std::multiplies<float_type>());
0890 std::transform(dxi.begin(), dxi.end(), dy.begin(), dydx.begin(),
0891 std::multiplies<float_type>());
0892
0893
0894 std::transform(dydx.begin()+1, dydx.end(), dydx.begin(), u.begin()+1,
0895 std::minus<float_type>() );
0896
0897 y2.resize(np,0.0);
0898
0899 if(lowerSlopeNatural) {
0900 y2[0]=u[0]=0.0;
0901 } else {
0902 y2[0]= -0.5;
0903 u[0]=(3.0*dxi[0])*(dy[0]*dxi[0] -lowerSlope);
0904 }
0905
0906 for(size_t i=1; i < np -1; i++) {
0907 float_type sig=siga[i-1];
0908 float_type p=sig*y2[i-1]+2.0;
0909 y2[i]=(sig-1.0)/p;
0910 u[i]=(6.0*u[i]*dx2i[i-1] - sig*u[i-1])/p;
0911 }
0912
0913 float_type qn, un;
0914
0915 if(upperSlopeNatural) {
0916 qn=un=0.0;
0917 } else {
0918 qn= 0.5;
0919 un=(3.0*dxi[dxi.size()-1])*(upperSlope- dy[dy.size()-1]*dxi[dxi.size()-1] );
0920 }
0921
0922 y2[np-1]=(un-qn*u[np-2])/(qn*y2[np-2]+1.0);
0923 for (size_t k=np-1; k != 0; k--) y2[k-1]=y2[k-1]*y2[k]+u[k-1];
0924 }
0925
0926 template <typename float_type> interpolating_function_p<float_type>
0927 &interpolating_function_p<float_type>::sample_function(
0928 const c2_function<float_type> &func,
0929 float_type amin, float_type amax, float_type abs_tol, float_type rel_tol,
0930 bool lowerSlopeNatural, float_type lowerSlope,
0931 bool upperSlopeNatural, float_type upperSlope
0932 )
0933 {
0934 c2_ptr<float_type> keepme(*this);
0935
0936 const c2_transformation<float_type> &XX=fTransform.X, &YY=fTransform.Y;
0937
0938
0939 sampler_function=func;
0940 std::vector<float_type> grid;
0941 func.get_sampling_grid(amin, amax, grid);
0942 size_t gsize=grid.size();
0943 if(XX.fTransformed) for(size_t i=0; i<gsize; i++) grid[i]=XX.fIn(grid[i]);
0944 this->set_sampling_grid_pointer(grid);
0945
0946 this->adaptively_sample(grid.front(), grid.back(), 8*abs_tol,
0947 8*rel_tol, 0, &X, &F);
0948
0949
0950 sampler_function.unset_function();
0951
0952 xInverted=this->check_monotonicity(X,
0953 "interpolating_function::init() non-monotonic transformed x input");
0954
0955 size_t np=X.size();
0956
0957
0958
0959 if(!XX.fTransformed) Xraw=X;
0960 else {
0961 Xraw.resize(np);
0962 for (size_t i=1; i<np-1; i++) Xraw[i]=XX.fOut(X[i]);
0963 Xraw.front()=amin;
0964 Xraw.back()=amax;
0965 }
0966
0967 bool xraw_rev=this->check_monotonicity(Xraw,
0968 "interpolating_function::init() non-monotonic raw x input");
0969
0970
0971 if(!xraw_rev) {
0972 this->set_sampling_grid_pointer(Xraw);
0973
0974 } else {
0975 this->set_sampling_grid(Xraw);
0976 }
0977
0978 if(XX.fTransformed) {
0979 if(!lowerSlopeNatural) lowerSlope /= XX.fInPrime(amin);
0980 if(!upperSlopeNatural) upperSlope /= XX.fInPrime(amax);
0981 }
0982 if(YY.fTransformed) {
0983 if(!lowerSlopeNatural) lowerSlope *= YY.fInPrime(func(amin));
0984 if(!upperSlopeNatural) upperSlope *= YY.fInPrime(func(amax));
0985 }
0986
0987
0988
0989
0990
0991
0992
0993
0994 if(lowerSlopeNatural) {
0995 float_type hlower=X[1]-X[0];
0996 lowerSlope=0.5*(-F[2]-3*F[0]+4*F[1])/hlower;
0997 lowerSlopeNatural=false;
0998 }
0999 if(upperSlopeNatural) {
1000 float_type hupper=X[np-1]-X[np-2];
1001 upperSlope=0.5*(F[np-3]+3*F[np-1]-4*F[np-2])/hupper;
1002 upperSlopeNatural=false;
1003 }
1004 this->set_domain(amin, amax);
1005
1006 spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope);
1007 lastKLow=0;
1008 keepme.release_for_return();
1009 return *this;
1010 }
1011
1012
1013
1014
1015 template <typename float_type> float_type
1016 interpolating_function_p<float_type>::value_with_derivatives(
1017 float_type x, float_type *yprime, float_type *yprime2) const
1018 {
1019 if(sampler_function.valid()) {
1020
1021
1022
1023 if(yprime) *yprime=0;
1024 if(yprime2) *yprime2=0;
1025 sampler_function->increment_evaluations();
1026 return fTransform.Y.fIn(sampler_function(fTransform.X.fOut(x)));
1027
1028 }
1029
1030 if(x < this->xmin() || x > this->xmax()) {
1031 std::ostringstream outstr;
1032 outstr << "Interpolating function argument " << x
1033 << " out of range " << this->xmin() << " -- " << this ->xmax() << ": bailing";
1034 throw c2_exception(outstr.str().c_str());
1035 }
1036
1037 float_type xraw=x;
1038
1039 if(fTransform.X.fTransformed) x=fTransform.X.fHasStaticTransforms?
1040 fTransform.X.pIn(x) : fTransform.X.fIn(x);
1041
1042
1043 int klo=0, khi=X.size()-1;
1044
1045 if(khi < 0) throw c2_exception(
1046 "Uninitialized interpolating function being evaluated");
1047
1048 const float_type *XX=&X[lastKLow];
1049
1050
1051 if(!xInverted) {
1052
1053 if((XX[0] <= x) && (XX[1] >= x) ) {
1054 klo=lastKLow;
1055 } else if((XX[1] <= x) && (XX[2] >= x)) {
1056 klo=lastKLow+1;
1057 } else if(lastKLow > 0 && (XX[-1] <= x) && (XX[0] >= x)) {
1058
1059 klo=lastKLow-1;
1060 } else {
1061
1062 while(khi-klo > 1) {
1063 int kk=(khi+klo)/2;
1064 if(X[kk] > x) khi=kk;
1065 else klo=kk;
1066 }
1067 }
1068 } else {
1069 if((XX[0] >= x) && (XX[1] <= x) ) {
1070 klo=lastKLow;
1071 } else if((XX[1] >= x) && (XX[2] <= x)) {
1072 klo=lastKLow+1;
1073 } else if(lastKLow > 0 && (XX[-1] >= x) && (XX[0] <= x)) {
1074
1075 klo=lastKLow-1;
1076 } else {
1077
1078 while(khi-klo > 1) {
1079 int kk=(khi+klo)/2;
1080 if(X[kk] < x) khi=kk;
1081 else klo=kk;
1082 }
1083 }
1084 }
1085
1086 khi=klo+1;
1087 lastKLow=klo;
1088
1089 float_type h=X[khi]-X[klo];
1090
1091 float_type a=(X[khi]-x)/h;
1092 float_type b=1.0-a;
1093 float_type ylo=F[klo], yhi=F[khi], y2lo=y2[klo], y2hi=y2[khi];
1094 float_type y=a*ylo+b*yhi+((a*a*a-a)*y2lo+(b*b*b-b)*y2hi)*(h*h)/6.0;
1095
1096 float_type yp0=0;
1097 float_type ypp0=0;
1098
1099 if(yprime || yprime2) {
1100 yp0=(yhi-ylo)/h+((3*b*b-1)*y2hi-(3*a*a-1)*y2lo)*h/6.0;
1101
1102 ypp0=b*y2hi+a*y2lo;
1103 }
1104
1105 if(fTransform.isIdentity) {
1106 if(yprime) *yprime=yp0;
1107 if(yprime2) *yprime2=ypp0;
1108 return y;
1109 } else return fTransform.evaluate(xraw, y, yp0, ypp0, yprime, yprime2);
1110 }
1111
1112 template <typename float_type> void
1113 interpolating_function_p<float_type>::set_lower_extrapolation(float_type bound)
1114 {
1115 int kl = 0 ;
1116 int kh=kl+1;
1117 float_type xx=fTransform.X.fIn(bound);
1118 float_type h0=X[kh]-X[kl];
1119 float_type h1=xx-X[kl];
1120 float_type yextrap=F[kl]+((F[kh]-F[kl])/h0
1121 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
1122
1123 X.insert(X.begin(), xx);
1124 F.insert(F.begin(), yextrap);
1125 y2.insert(y2.begin(), y2.front());
1126 Xraw.insert(Xraw.begin(), bound);
1127 if (bound < this->fXMin) this->fXMin=bound;
1128 else this->fXMax=bound;
1129
1130 }
1131
1132 template <typename float_type> void
1133 interpolating_function_p<float_type>::set_upper_extrapolation(float_type bound)
1134 {
1135 int kl = X.size()-2 ;
1136 int kh=kl+1;
1137 float_type xx=fTransform.X.fIn(bound);
1138 float_type h0=X[kh]-X[kl];
1139 float_type h1=xx-X[kl];
1140 float_type yextrap=F[kl]+((F[kh]-F[kl])/h0
1141 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
1142
1143 X.insert(X.end(), xx);
1144 F.insert(F.end(), yextrap);
1145 y2.insert(y2.end(), y2.back());
1146 Xraw.insert(Xraw.end(), bound);
1147 if (bound < this->fXMin) this->fXMin=bound;
1148 else this->fXMax=bound;
1149
1150
1151 }
1152
1153 template <typename float_type> interpolating_function_p<float_type>&
1154 interpolating_function_p<float_type>::unary_operator(
1155 const c2_function<float_type> &source) const
1156 {
1157 size_t np=X.size();
1158 std::vector<float_type>yv(np);
1159 c2_ptr<float_type> comp(source(*this));
1160 float_type yp0, yp1, ypp;
1161
1162 for(size_t i=1; i<np-1; i++) {
1163 yv[i]=source(fTransform.Y.fOut(F[i]));
1164 }
1165
1166 yv.front()=comp(Xraw.front(), &yp0, &ypp);
1167 yv.back()= comp(Xraw.back(), &yp1, &ypp);
1168
1169 interpolating_function_p ©=clone();
1170 copy.load(this->Xraw, yv, false, yp0, false, yp1);
1171
1172 return copy;
1173 }
1174
1175 template <typename float_type> void
1176 interpolating_function_p<float_type>::get_data(std::vector<float_type> &xvals,
1177 std::vector<float_type> &yvals) const
1178 {
1179
1180 xvals=Xraw;
1181 yvals.resize(F.size());
1182
1183 for(size_t i=0; i<F.size(); i++) yvals[i]=fTransform.Y.fOut(F[i]);
1184 }
1185
1186 template <typename float_type> interpolating_function_p<float_type> &
1187 interpolating_function_p<float_type>::binary_operator(const
1188 c2_function<float_type> &rhs,
1189 const c2_binary_function<float_type> *combining_stub) const
1190 {
1191 size_t np=X.size();
1192 std::vector<float_type> yv(np);
1193 c2_constant_p<float_type> fval(0);
1194 float_type yp0, yp1, ypp;
1195
1196 c2_const_ptr<float_type> stub(*combining_stub);
1197
1198 for(size_t i=1; i<np-1; i++) {
1199 fval.reset(fTransform.Y.fOut(F[i]));
1200 yv[i]=combining_stub->combine(fval, rhs, Xraw[i], (float_type *)0,
1201 (float_type *)0);
1202 }
1203
1204 yv.front()=combining_stub->combine(*this, rhs, Xraw.front(), &yp0, &ypp);
1205 yv.back()= combining_stub->combine(*this, rhs, Xraw.back(), &yp1, &ypp);
1206
1207 interpolating_function_p ©=clone();
1208 copy.load(this->Xraw, yv, false, yp0, false, yp1);
1209
1210 return copy;
1211 }
1212
1213 template <typename float_type>
1214 c2_inverse_function_p<float_type>::c2_inverse_function_p(
1215 const c2_function<float_type> &source)
1216 : c2_function<float_type>(), func(source)
1217 {
1218 float_type l=source.xmin();
1219 float_type r=source.xmax();
1220 start_hint=(l+r)*0.5;
1221
1222 float_type ly=source(l);
1223 float_type ry=source(r);
1224 if (ly > ry) {
1225 float_type t=ly; ly=ry; ry=t;
1226 }
1227 this->set_domain(ly, ry);
1228 }
1229
1230 template <typename float_type> float_type
1231 c2_inverse_function_p<float_type>::value_with_derivatives(
1232 float_type x, float_type *yprime, float_type *yprime2
1233 ) const
1234 {
1235 float_type l=this->func->xmin();
1236 float_type r=this->func->xmax();
1237 float_type yp, ypp;
1238 float_type y=this->func->find_root(l, r, get_start_hint(x), x, 0, &yp, &ypp);
1239 start_hint=y;
1240 if(yprime) *yprime=1.0/yp;
1241 if(yprime2) *yprime2=-ypp/(yp*yp*yp);
1242 return y;
1243 }
1244
1245 template <typename float_type>
1246 accumulated_histogram<float_type>::accumulated_histogram(
1247 const std::vector<float_type>binedges, const std::vector<float_type> binheights,
1248 bool normalize, bool inverse_function, bool drop_zeros)
1249 {
1250
1251 int np=binheights.size();
1252
1253 std::vector<float_type> be, bh;
1254 if(drop_zeros || inverse_function) {
1255 if(binheights[0] || !inverse_function) {
1256 be.push_back(binedges[0]);
1257 bh.push_back(binheights[0]);
1258 }
1259 for(int i=1; i<np-1; i++) {
1260 if(binheights[i]) {
1261 be.push_back(binedges[i]);
1262 bh.push_back(binheights[i]);
1263 }
1264 }
1265 if(binheights[np-1] || !inverse_function) {
1266 bh.push_back(binheights[np-1]);
1267 be.push_back(binedges[np-1]);
1268 be.push_back(binedges[np]);
1269 }
1270 np=bh.size();
1271 } else {
1272 be=binedges;
1273 bh=binheights;
1274 }
1275 std::vector<float_type> cum(np+1, 0.0);
1276 for(int i=1; i<=np; i++) cum[i]=bh[i]*(be[i]-be[i-1])+cum[i-1];
1277
1278 if(be[1] < be[0]) {
1279 std::reverse(be.begin(), be.end());
1280 std::reverse(cum.begin(), cum.end());
1281 for(unsigned int i=0; i<cum.size(); i++) cum[i]*=-1;
1282
1283 }
1284 if(normalize) {
1285 float_type mmm=1.0/std::max(cum[0], cum[np]);
1286 for(int i=0; i<=np; i++) cum[i]*=mmm;
1287 }
1288 if(inverse_function) interpolating_function_p<float_type>(cum, be);
1289
1290 else interpolating_function_p<float_type>(be, cum);
1291
1292 std::fill(this->y2.begin(), this->y2.end(), 0.0);
1293
1294 }
1295
1296 template <typename float_type>
1297 c2_piecewise_function_p<float_type>::c2_piecewise_function_p()
1298 : c2_function<float_type>(), lastKLow(-1)
1299 {
1300 this->sampling_grid=new std::vector<float_type>;
1301 }
1302
1303 template <typename float_type>
1304 c2_piecewise_function_p<float_type>::~c2_piecewise_function_p()
1305 {
1306 }
1307
1308 template <typename float_type> float_type
1309 c2_piecewise_function_p<float_type>::value_with_derivatives(
1310 float_type x, float_type *yprime, float_type *yprime2
1311 ) const
1312 {
1313
1314 size_t np=functions.size();
1315 if(!np) throw c2_exception(
1316 "attempting to evaluate an empty piecewise function");
1317
1318 if(x < this->xmin() || x > this->xmax()) {
1319 std::ostringstream outstr;
1320 outstr << "piecewise function argument " << x << " out of range "
1321 << this->xmin() << " -- " << this->xmax();
1322 throw c2_exception(outstr.str().c_str());
1323 }
1324
1325 int klo=0;
1326
1327 if(lastKLow >= 0 && functions[lastKLow]->xmin() <= x
1328 && functions[lastKLow]->xmax() > x) {
1329 klo=lastKLow;
1330 } else {
1331 int khi=np;
1332 while(khi-klo > 1) {
1333 int kk=(khi+klo)/2;
1334 if(functions[kk]->xmin() > x) khi=kk;
1335 else klo=kk;
1336 }
1337 }
1338 lastKLow=klo;
1339 return functions[klo]->value_with_derivatives(x, yprime, yprime2);
1340 }
1341
1342 template <typename float_type> void
1343 c2_piecewise_function_p<float_type>::append_function(
1344 const c2_function<float_type> &func)
1345 {
1346 c2_const_ptr<float_type> keepfunc(func);
1347 if(functions.size()) {
1348 const c2_function<float_type> &tail=functions.back();
1349 float_type x0=tail.xmax();
1350 float_type x1=func.xmin();
1351 if(x0 < x1) {
1352
1353 float_type y0=tail(x0);
1354 float_type y1=func(x1);
1355 c2_function<float_type> &connector=
1356 *new c2_linear_p<float_type>(x0, y0, (y1-y0)/(x1-x0));
1357 connector.set_domain(x0,x1);
1358 functions.push_back(c2_const_ptr<float_type>(connector));
1359 this->sampling_grid->push_back(x1);
1360 } else if(x0>x1) throw c2_exception(
1361 "function domains not increasing in c2_piecewise_function");
1362 }
1363 functions.push_back(keepfunc);
1364
1365 this->set_domain(functions.front()->xmin(), functions.back()->xmax());
1366
1367
1368 std::vector<float_type> newgrid;
1369 func.get_sampling_grid(func.xmin(), func.xmax(), newgrid);
1370 this->sampling_grid->insert(this->sampling_grid->end(),
1371 newgrid.begin()+1, newgrid.end());
1372 }
1373
1374 template <typename float_type>
1375 c2_connector_function_p<float_type>::c2_connector_function_p(
1376 float_type x0, const c2_function<float_type> &f0, float_type x2,
1377 const c2_function<float_type> &f2,
1378 bool auto_center, float_type y1)
1379 : c2_function<float_type>()
1380 {
1381 c2_const_ptr<float_type> left(f0), right(f2);
1382 c2_fblock<float_type> fb0, fb2;
1383 fb0.x=x0;
1384 f0.fill_fblock(fb0);
1385 fb2.x=x2;
1386 f2.fill_fblock(fb2);
1387 init(fb0, fb2, auto_center, y1);
1388 }
1389
1390 template <typename float_type>
1391 c2_connector_function_p<float_type>::c2_connector_function_p(
1392 float_type x0, float_type y0, float_type yp0, float_type ypp0,
1393 float_type x2, float_type y2, float_type yp2, float_type ypp2,
1394 bool auto_center, float_type y1)
1395 : c2_function<float_type>()
1396 {
1397 c2_fblock<float_type> fb0, fb2;
1398 fb0.x=x0; fb0.y=y0; fb0.yp=yp0; fb0.ypp=ypp0;
1399 fb2.x=x2; fb2.y=y2; fb2.yp=yp2; fb2.ypp=ypp2;
1400 init(fb0, fb2, auto_center, y1);
1401 }
1402
1403 template <typename float_type>
1404 c2_connector_function_p<float_type>::c2_connector_function_p(
1405 const c2_fblock<float_type> &fb0,
1406 const c2_fblock<float_type> &fb2,
1407 bool auto_center, float_type y1)
1408 : c2_function<float_type>()
1409 {
1410 init(fb0, fb2, auto_center, y1);
1411 }
1412
1413 template <typename float_type> void c2_connector_function_p<float_type>::init(
1414 const c2_fblock<float_type> &fb0,
1415 const c2_fblock<float_type> &fb2,
1416 bool auto_center, float_type y1)
1417 {
1418 float_type dx=(fb2.x-fb0.x)/2.0;
1419 fhinv=1.0/dx;
1420
1421
1422 float_type yp0=fb0.yp*dx;
1423 float_type yp2=fb2.yp*dx;
1424 float_type ypp0=fb0.ypp*dx*dx;
1425 float_type ypp2=fb2.ypp*dx*dx;
1426
1427 float_type ff0=(8*(fb0.y + fb2.y) + 5*(yp0 - yp2) + ypp0 + ypp2)*0.0625;
1428 if(auto_center) y1=ff0;
1429
1430
1431
1432
1433
1434
1435
1436 fy1=y1;
1437 fa=(fb2.y - fb0.y)*0.5;
1438 fb=(fb0.y + fb2.y)*0.5 - y1;
1439 fc=(yp2+yp0-2.*fa)*0.25;
1440 fd=(yp2-yp0-4.*fb)*0.25;
1441 fe=(ypp2-ypp0-12.*fc)*0.0625;
1442 ff=(ff0 - y1);
1443 this->set_domain(fb0.x, fb2.x);
1444 }
1445
1446 template <typename float_type>
1447 c2_connector_function_p<float_type>::~c2_connector_function_p()
1448 {
1449 }
1450
1451 template <typename float_type> float_type
1452 c2_connector_function_p<float_type>::value_with_derivatives(
1453 float_type x, float_type *yprime, float_type *yprime2
1454 ) const
1455 {
1456 float_type x0=this->xmin(), x2=this->xmax();
1457 float_type dx=(x-(x0+x2)*0.5)*fhinv;
1458 float_type q1=(x-x0)*(x-x2)*fhinv*fhinv;
1459 float_type q2=dx*q1;
1460
1461 float_type r1=fa+fb*dx;
1462 float_type r2=fc+fd*dx;
1463 float_type r3=fe+ff*dx;
1464
1465 float_type y=fy1+dx*r1+q2*r2+q1*q2*r3;
1466
1467 if(yprime || yprime2) {
1468 float_type q3=3*q1+2;
1469 float_type q4=5*q1+4;
1470 if(yprime) *yprime=(fa+2*fb*dx+fd*q2+r2*q3+ff*q1*q2+q1*q4*r3)*fhinv;
1471 if(yprime2) *yprime2=2*(fb+fd*q3+3*dx*r2+ff*q1*q4
1472 +r3*(2*dx*(5*q1+2)))*fhinv*fhinv;
1473 }
1474 return y;
1475 }
1476
1477
1478
1479 template <typename float_type> void
1480 c2_function<float_type>::sample_step(c2_sample_recur &rb) const
1481
1482 {
1483 std::vector< recur_item > &rb_stack=*rb.rb_stack;
1484
1485 rb_stack.clear();
1486
1487 recur_item top;
1488 top.depth=0; top.done=false; top.f0index=0; top.f2index=0;
1489
1490
1491 rb_stack.push_back(top);
1492 rb_stack.back().f1=*rb.f0;
1493 rb_stack.back().done=true;
1494
1495 rb_stack.push_back(top);
1496 rb_stack.back().f1=*rb.f1;
1497 rb_stack.back().done=true;
1498
1499 if(!rb.inited) {
1500 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
1501 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
1502 rb.inited=true;
1503 }
1504
1505
1506 top.f0index=0;
1507 top.f2index=1;
1508 rb_stack.push_back(top);
1509
1510 while(rb_stack.size() > 2) {
1511 recur_item &back=rb_stack.back();
1512 if(back.done) {
1513 rb_stack.pop_back();
1514 continue;
1515 }
1516 back.done=true;
1517
1518 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1,
1519 &f2=rb_stack[back.f2index].f1;
1520 c2_fblock<float_type> &f1=back.f1;
1521 size_t f1index=rb_stack.size()-1;
1522
1523
1524 f1.x=0.5*(f0.x + f2.x);
1525 float_type dx2=0.5*(f2.x - f0.x);
1526
1527 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2)
1528 < rb.abs_tol_min) {
1529 std::ostringstream outstr;
1530 outstr << "Step size underflow in adaptive_sampling at depth="
1531 << back.depth << ", x= " << f1.x;
1532 throw c2_exception(outstr.str().c_str());
1533 }
1534
1535 fill_fblock(f1);
1536
1537 if(c2_isnan(f1.y) || f1.ypbad || f1.yppbad) {
1538
1539 bad_x_point=f1.x;
1540 throw c2_exception("NaN encountered while sampling function");
1541 }
1542
1543 float_type eps;
1544 if(rb.derivs==2) {
1545
1546
1547
1548 float_type ff0=(8*(f0.y + f2.y) + 5*(f0.yp - f2.yp)*dx2
1549 + (f0.ypp+f2.ypp)*dx2*dx2)*0.0625;
1550
1551
1552 eps=std::abs(ff0-f1.y)/32.0;
1553 } else {
1554
1555
1556
1557
1558 float_type ypcenter, ypp;
1559 if (rb.derivs==1) {
1560
1561 eps = (std::abs(f0.y+f0.yp*dx2-f1.y)+std::abs(f2.y-f2.yp*dx2-f1.y))*0.125;
1562 ypcenter=2*f1.yp*dx2;
1563 ypp=2*(f2.yp-f0.yp)*dx2*dx2;
1564 } else {
1565
1566
1567 ypcenter=(f2.y-f0.y)*0.5;
1568 ypp=(f2.y+f0.y-2*f1.y);
1569 if(back.depth==0) eps=std::abs((f0.y+f2.y)*0.5 - f1.y)*2;
1570
1571 else eps=std::abs(f1.y-back.previous_estimate)*0.25;
1572 }
1573 float_type ypleft=ypcenter-ypp;
1574 float_type ypright=ypcenter+ypp;
1575 float_type extremum_eps=0;
1576 if((ypleft*ypright) <=0)
1577 {
1578
1579 float_type xext=-ypcenter/ypp;
1580 float_type yext=f1.y + xext*ypcenter + 0.5*xext*xext*ypp;
1581
1582
1583 if(xext <=0) extremum_eps=std::min(std::abs(f0.y-yext), std::abs(f1.y-yext));
1584 else extremum_eps=std::min(std::abs(f2.y-yext), std::abs(f1.y-yext));
1585 }
1586 eps=std::max(eps, extremum_eps);
1587 }
1588
1589 if(eps < rb.abs_tol || eps < std::abs(f1.y)*rb.rel_tol) {
1590 if(rb.out) {
1591
1592 rb.out->append_function(
1593 *new c2_connector_function_p<float_type>(f0, f1, true, 0.0)
1594 );
1595 rb.out->append_function(
1596 *new c2_connector_function_p<float_type>(f1, f2, true, 0.0)
1597 );
1598 }
1599 if(rb.xvals && rb.yvals) {
1600 rb.xvals->push_back(f0.x);
1601 rb.xvals->push_back(f1.x);
1602 rb.yvals->push_back(f0.y);
1603 rb.yvals->push_back(f1.y);
1604
1605 }
1606 } else {
1607 top.depth=back.depth+1;
1608
1609
1610
1611 size_t f0index=back.f0index, f2index=back.f2index;
1612 float_type left=0, right=0;
1613 if(rb.derivs==0) {
1614
1615
1616 left=(6*f1.y + 3*f0.y - f2.y) * 0.125;
1617 right=(6*f1.y + 3*f2.y - f0.y) * 0.125;
1618 }
1619
1620 top.f0index=f1index; top.f2index=f2index;
1621
1622 top.previous_estimate=right;
1623 rb_stack.push_back(top);
1624
1625 top.f0index=f0index; top.f2index=f1index;
1626
1627 top.previous_estimate=left;
1628 rb_stack.push_back(top);
1629 }
1630 }
1631 }
1632
1633 template <typename float_type> c2_piecewise_function_p<float_type> *
1634 c2_function<float_type>::adaptively_sample(
1635 float_type amin, float_type amax,
1636 float_type abs_tol, float_type rel_tol,
1637 int derivs, std::vector<float_type> *xvals,
1638 std::vector<float_type> *yvals) const
1639 {
1640 c2_fblock<float_type> f0, f2;
1641 c2_sample_recur rb;
1642 std::vector< recur_item > rb_stack;
1643 rb_stack.reserve(20);
1644 rb.rb_stack=&rb_stack;
1645 rb.out=0;
1646 if(derivs==2) rb.out=new c2_piecewise_function_p<float_type>();
1647 c2_ptr<float_type> pieces(*rb.out);
1648 rb.rel_tol=rel_tol;
1649 rb.abs_tol=abs_tol;
1650 rb.xvals=xvals;
1651 rb.yvals=yvals;
1652 rb.derivs=derivs;
1653 rb.inited=false;
1654
1655 if(xvals && yvals) {
1656 xvals->clear();
1657 yvals->clear();
1658 }
1659
1660
1661
1662
1663 std::vector<float_type> xgrid;
1664 get_sampling_grid(amin, amax, xgrid);
1665 int np=xgrid.size();
1666
1667 f2.x=xgrid[0];
1668 fill_fblock(f2);
1669 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1670
1671 bad_x_point=f2.x;
1672 throw c2_exception("NaN encountered while sampling function");
1673 }
1674
1675 for(int i=0; i<np-1; i++) {
1676 f0=f2;
1677
1678 f2.x=xgrid[i+1];
1679 fill_fblock(f2);
1680 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1681
1682 bad_x_point=f2.x;
1683 throw c2_exception("NaN encountered while sampling function");
1684 }
1685
1686 rb.f0=&f0; rb.f1=&f2;
1687 sample_step(rb);
1688 }
1689 if(xvals && yvals) {
1690 xvals->push_back(f2.x);
1691 yvals->push_back(f2.y);
1692 }
1693
1694 if(rb.out) rb.out->set_sampling_grid(xgrid);
1695
1696 pieces.release_for_return();
1697
1698 return rb.out;
1699 }