Geant4  10.03
c2_function.icc
Go to the documentation of this file.
1 //
2 // ********************************************************************
3 // * License and Disclaimer *
4 // * *
5 // * The Geant4 software is copyright of the Copyright Holders of *
6 // * the Geant4 Collaboration. It is provided under the terms and *
7 // * conditions of the Geant4 Software License, included in the file *
8 // * LICENSE and available at http://cern.ch/geant4/license . These *
9 // * include a list of copyright holders. *
10 // * *
11 // * Neither the authors of this software system, nor their employing *
12 // * institutes,nor the agencies providing financial support for this *
13 // * work make any representation or warranty, express or implied, *
14 // * regarding this software system or assume any liability for its *
15 // * use. Please see the license in the file LICENSE and URL above *
16 // * for the full disclaimer and the limitation of liability. *
17 // * *
18 // * This code implementation is the result of the scientific and *
19 // * technical work of the GEANT4 collaboration. *
20 // * By using, copying, modifying or distributing the software (or *
21 // * any work based on the software) you agree to acknowledge its *
22 // * use in resulting scientific publications, and indicate your *
23 // * acceptance of all terms of the Geant4 Software license. *
24 // ********************************************************************
25 //
26 //
27 /**
28  * \file electromagnetic/TestEm7/include/c2_function.icc
29  * \brief Provides code for the general c2_function algebra which supports
30  * fast, flexible operations on piecewise-twice-differentiable functions
31  *
32  * \author Created by R. A. Weller and Marcus H. Mendenhall on 7/9/05.
33  * \author Copyright 2005 __Vanderbilt University__. All rights reserved.
34  *
35  *\version c2_function.cc 488 2012-04-04 11:30:21Z marcus
36  */
37 
38  // $Id$
39  //
40 
41 #include <iostream>
42 #include <vector>
43 #include <algorithm>
44 #include <cstdlib>
45 #include <numeric>
46 #include <functional>
47 #include <iterator>
48 #include <cmath>
49 #include <limits>
50 #include <sstream>
51 
52 template <typename float_type> const std::string
53  c2_function<float_type>::cvs_file_vers() const
54 { return "c2_function.cc 488 2012-04-04 11:30:21Z marcus "; }
55 
56 template <typename float_type> float_type
57  c2_function<float_type>::find_root(float_type lower_bracket,
58  float_type upper_bracket,
59  float_type start, float_type value, int *error,
60  float_type *final_yprime, float_type *final_yprime2) const
61  throw(c2_exception)
62 {
63 // find f(x)=value within the brackets, using the guarantees of
64 // smoothness associated with a c2_function
65 // can use local f(x)=a*x**2 + b*x + c and solve quadratic to find root,
66 // then iterate
67 
68  float_type yp, yp2;
69  if (!final_yprime) final_yprime=&yp;
70  if (!final_yprime2) final_yprime2=&yp2;
71 
72  float_type ftol=5*(std::numeric_limits<float_type>::epsilon()
73  *std::abs(value)+std::numeric_limits<float_type>::min());
74  float_type xtol=5*(std::numeric_limits<float_type>::epsilon()
75  *(std::abs(upper_bracket)+std::abs(lower_bracket))
76  +std::numeric_limits<float_type>::min());
77 
78  float_type root=start; // start looking in the middle
79  if(error) *error=0; // start out with error flag set to OK, if it is expected
80 
81  float_type c, b;
82  if(!root_info) {
83  root_info=new struct c2_root_info;
84  root_info->inited=false;
85  }
86 // this new logic is to keep track of where we were before,
87 // and lower the number of
88 // function evaluations if we are searching inside the same bracket as before.
89 // Since this root finder has, very often, the bracket of the entire
90 // domain of the function,
91 // this makes a big difference, especially to c2_inverse_function
92  if(!root_info->inited || upper_bracket != root_info->upper.x
93  || lower_bracket != root_info->lower.x) {
94  root_info->upper.x=upper_bracket;
95  fill_fblock(root_info->upper);
96  root_info->lower.x=lower_bracket;
97  fill_fblock(root_info->lower);
98  root_info->inited=true;
99  }
100 
101  float_type clower=root_info->lower.y-value;
102  if(!clower) {
103  *final_yprime=root_info->lower.yp;
104  *final_yprime2=root_info->lower.ypp;
105  return lower_bracket;
106  }
107 
108  float_type cupper=root_info->upper.y-value;
109 if(!cupper) {
110 *final_yprime=root_info->upper.yp;
111 *final_yprime2=root_info->upper.ypp;
112 return upper_bracket;
113 }
114 const float_type lower_sign = (clower < 0) ? -1 : 1;
115 
116 if(lower_sign*cupper >0) {
117 // argh, no sign change in here!
118 if(error) { *error=1; return 0.0; }
119 else {
120 std::ostringstream outstr;
121 outstr << "unbracketed root in find_root at xlower= " << lower_bracket
122  << ", xupper= " << upper_bracket;
123 outstr << ", value= " << value << ": bailing";
124 throw c2_exception(outstr.str().c_str());
125 }
126 }
127 
128  float_type delta=upper_bracket-lower_bracket; // first error step
129  c=value_with_derivatives(root, final_yprime, final_yprime2)-value;
130  b=*final_yprime; // make a local copy for readability
131  increment_evaluations();
132 
133  while(
134  std::abs(delta) > xtol && // absolute x step check
135  std::abs(c) > ftol && // absolute y tolerance
136  std::abs(c) > xtol*std::abs(b)
137  )
138 {
139  float_type a=(*final_yprime2)/2; // second derivative is 2*a
140  float_type disc=b*b-4*a*c;
141  // std::cout << std::endl << "find_root_debug a,b,c,d " << a
142  //<< " " << b << " " << c << " " << disc << std::endl;
143 
144  if(disc >= 0) {
145  float_type q=-0.5*((b>=0)?(b+std::sqrt(disc)):(b-std::sqrt(disc)));
146  if(q*q > std::abs(a*c)) delta=c/q;
147  // since x1=q/a, x2=c/q, x1/x2=q^2/ac, this picks smaller step
148  else delta=q/a;
149  root+=delta;
150  }
151 
152  if(disc < 0 || root<lower_bracket || root>upper_bracket ||
153  std::abs(delta) >= 0.5*(upper_bracket-lower_bracket)) {
154  // if we jump out of the bracket, or aren't converging well, bisect
155  root=0.5*(lower_bracket+upper_bracket);
156  delta=upper_bracket-lower_bracket;
157  }
158  c=value_with_derivatives(root, final_yprime, final_yprime2)-value;
159  // compute initial values
160  if(c2_isnan(c)) {
161  bad_x_point=root;
162  return c; // return the nan if a computation failed
163  }
164  b=*final_yprime; // make a local copy for readability
165  increment_evaluations();
166 
167  // now, close in bracket on whichever side this still brackets
168  if(c*lower_sign < 0.0) {
169  cupper=c;
170  upper_bracket=root;
171  } else {
172  clower=c;
173  lower_bracket=root;
174  }
175  // std::cout << "find_root_debug x, y, dx " << root << " "
176  // << c << " " << delta << std::endl;
177  }
178  return root;
179 }
180 
181 /* def partial_integrals(self, xgrid):
182 Return the integrals of a function between the sampling points xgrid.
183 The sum is the definite integral.
184 This method uses an exact integration of the polynomial which matches
185 the values and derivatives at the
186 endpoints of a segment. Its error scales as h**6, if the input functions
187 really are smooth.
188 This could very well be used as a stepper for adaptive Romberg integration.
189 For InterpolatingFunctions, it is likely that the Simpson's rule integrator
190 is sufficient.
191 #the weights come from an exact mathematica solution to the 5th order
192 polynomial with the given values & derivatives
193 #yint = (y0+y1)*dx/2 + dx^2*(yp0-yp1)/10 + dx^3 * (ypp0+ypp1)/120 )
194 */
195 
196 // the recursive part of the integrator is agressively designed to
197 // minimize copying of data... lots of pointers
198 template <typename float_type> float_type
199 c2_function<float_type>::integrate_step(c2_integrate_recur &rb) const
200 throw(c2_exception)
201 {
202 std::vector< recur_item > &rb_stack=*rb.rb_stack;
203 // heap-based stack of data for recursion
204 rb_stack.clear();
205 
206 recur_item top;
207 top.depth=0; top.done=false; top.f0index=0; top.f2index=0; top.step_sum=0;
208 
209 // push storage for our initial elements
210 rb_stack.push_back(top);
211 rb_stack.back().f1=*rb.f0;
212 rb_stack.back().done=true; // this element will never be evaluated further
213 
214 rb_stack.push_back(top);
215 rb_stack.back().f1=*rb.f1;
216 rb_stack.back().done=true; // this element will never be evaluated further
217 
218 if(!rb.inited) {
219 switch(rb.derivs) {
220 case 0:
221 rb.eps_scale=0.1; rb.extrap_coef=16; break;
222 case 1:
223 rb.eps_scale=0.1; rb.extrap_coef=64; break;
224 case 2:
225 rb.eps_scale=0.02; rb.extrap_coef=1024; break;
226 default:
227 throw c2_exception("derivs must be 0, 1 or 2 in partial_integrals");
228 }
229 
230 rb.extrap2=1.0/(rb.extrap_coef-1.0);
231 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
232 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
233 rb.inited=true;
234 }
235 
236 // now, push our first real element
237 top.f0index=0; // left element is stack[0]
238 top.f2index=1; // right element is stack[1]
239 top.abs_tol=rb.abs_tol;
240 rb_stack.push_back(top);
241 
242 while(rb_stack.size() > 2) {
243 recur_item &back=rb_stack.back();
244 if(back.done) {
245 float_type sum=back.step_sum;
246 rb_stack.pop_back();
247 rb_stack.back().step_sum+=sum; // bump our sum up to the parent
248 continue;
249 }
250 back.done=true;
251 
252 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1,
253 &f2=rb_stack[back.f2index].f1;
254 c2_fblock<float_type> &f1=back.f1; // will hold new middle values
255 size_t f1index=rb_stack.size()-1; // our current offset
256 float_type abs_tol=back.abs_tol;
257 
258 f1.x=0.5*(f0.x + f2.x); // center of interval
259 float_type dx2=0.5*(f2.x - f0.x);
260 
261 // check for underflow on step size
262 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance
263 || std::abs(dx2) < rb.abs_tol_min) {
264 std::ostringstream outstr;
265 outstr << "Step size underflow in adaptive_partial_integrals at depth="
266  << back.depth << ", x= " << f1.x;
267 throw c2_exception(outstr.str().c_str());
268 }
269 
270 fill_fblock(f1);
271 if(c2_isnan(f1.y)) {
272 bad_x_point=f1.x;
273 return f1.y; // can't go any further if a nan has appeared
274 }
275 
276 bool yptrouble=f0.ypbad || f2.ypbad || f1.ypbad;
277 bool ypptrouble=f0.yppbad || f2.yppbad || f1.yppbad;
278 
279 // select the real derivative count based on whether we are at
280 // a point where derivatives exist
281 int derivs = std::min(rb.derivs, (yptrouble||ypptrouble)?(yptrouble?0:1):2);
282 
283 if(!back.depth) { // top level, total has not been initialized yet
284 switch(derivs) { // create estimate of next lower order for first try
285 case 0:
286 back.previous_estimate=(f0.y+f2.y)*dx2; break;
287 case 1:
288 back.previous_estimate=(f0.y+4.0*f1.y+f2.y)*dx2/3.0; break;
289 case 2:
290 back.previous_estimate=( (14*f0.y + 32*f1.y + 14*f2.y)
291 + 2*dx2 * (f0.yp - f2.yp) ) * dx2 /30.; break;
292 default:
293 back.previous_estimate=0.0; // just to suppress missing default warnings
294 }
295 }
296 
297 float_type left, right;
298 
299 // pre-compute constants so all multiplies use a small dynamic range
300 // note the forced csting of the denominators to assure full precision
301 // for (e.g.) long double
302 // constants for 0 derivative integrator
303 static const float_type c0c1=5./((float_type)12.),
304 c0c2=8./((float_type)12.), c0c3=-1./((float_type)12.);
305 // constants for 1 derivative integrator
306 static const float_type c1c1=101./((float_type)240.),
307 c1c2=128./((float_type)240.), c1c3=11./((float_type)240.),
308 c1c4=13./((float_type)240.), c1c5=-40./((float_type)240.),
309 c1c6=-3./((float_type)240.);
310 // constants for 2 derivative integrator
311 static const float_type c2c1=169./((float_type)40320.),
312 c2c2=1024./ ((float_type)40320.), c2c3=-41./((float_type)40320.),
313 c2c4=2727./((float_type)40320.), c2c5=-5040./((float_type)40320.),
314 c2c6=423./((float_type)40320.),
315 c2c7=17007./((float_type)40320.), c2c8=24576./((float_type)40320.),
316 c2c9=-1263./((float_type)40320.);
317 
318 switch(derivs) {
319 case 2:
320 // use ninth-order estimates for each side, from full set of all values (!)
321 left=( ( (c2c1*f0.ypp + c2c2*f1.ypp + c2c3*f2.ypp)*dx2 +
322 (c2c4*f0.yp + c2c5*f1.yp + c2c6*f2.yp) )*dx2 +
323  (c2c7*f0.y + c2c8*f1.y + c2c9*f2.y) )* dx2;
324 right=( ( (c2c1*f2.ypp + c2c2*f1.ypp + c2c3*f0.ypp)*dx2 -
325 (c2c4*f2.yp + c2c5*f1.yp + c2c6*f0.yp) )*dx2 +
326  (c2c7*f2.y + c2c8*f1.y + c2c9*f0.y) )* dx2;
327 // std::cout << f0.x << " " << f1.x << " " << f2.x << std::endl ;
328 // std::cout << f0.y << " " << f1.y << " " << f2.y << " " << left
329 // << " " << right << " " << total << std::endl ;
330 break;
331 case 1:
332 left= ( (c1c1*f0.y + c1c2*f1.y + c1c3*f2.y)
333 + dx2*(c1c4*f0.yp + c1c5*f1.yp + c1c6*f2.yp) ) * dx2 ;
334 right= ( (c1c1*f2.y + c1c2*f1.y + c1c3*f0.y)
335 - dx2*(c1c4*f2.yp + c1c5*f1.yp + c1c6*f0.yp) ) * dx2 ;
336 break;
337 case 0:
338 left=(c0c1*f0.y + c0c2*f1.y + c0c3*f2.y)*dx2;
339 right=(c0c1*f2.y + c0c2*f1.y + c0c3*f0.y)*dx2;
340 break;
341 default:
342 left=right=0.0; // suppress warnings about missing default
343 break;
344 }
345 
346 float_type lrsum=left+right;
347 
348 bool extrapolate=back.depth && rb.extrapolate && (derivs==rb.derivs);
349 // only extrapolate if no trouble with derivs
350 float_type eps=std::abs(back.previous_estimate-lrsum)*rb.eps_scale;
351 if(extrapolate) eps*=rb.eps_scale;
352 
353 if(rb.adapt && eps > abs_tol && eps > std::abs(lrsum)*rb.rel_tol) {
354 // tolerance not met, subdivide & recur
355 if(abs_tol > rb.abs_tol_min) abs_tol=abs_tol*0.5;
356 // each half has half the error budget
357 top.abs_tol=abs_tol;
358 top.depth=back.depth+1;
359 
360 // save the last things we need from back before a push happens, in case
361 // the push causes a reallocation and moves the whole stack.
362 size_t f0index=back.f0index, f2index=back.f2index;
363 
364 top.f0index=f1index; top.f2index=f2index;
365 // insert pointers to right side data into our recursion block
366 top.previous_estimate=right;
367 rb_stack.push_back(top);
368 
369 top.f0index=f0index; top.f2index=f1index;
370 // insert pointers to left side data into our recursion block
371 top.previous_estimate=left;
372 rb_stack.push_back(top);
373 
374 } else if(extrapolate) {
375 // extrapolation only happens on leaf nodes, where the tolerance was met.
376 back.step_sum+=(rb.extrap_coef*lrsum - back.previous_estimate)*rb.extrap2;
377 } else {
378 back.step_sum+=lrsum;
379 }
380 }
381 return rb_stack.back().step_sum; // last element on the stack holds the sum
382 }
383 
384 template <typename float_type> bool c2_function<float_type>::check_monotonicity(
385 const std::vector<float_type> &data,
386 const char message[]) const throw(c2_exception)
387 {
388 size_t np=data.size();
389 if(np < 2) return false; // one point has no direction!
390 
391 bool rev=(data[1] < data[0]); // which way do data point?
392 size_t i;
393 
394 if(!rev) for(i = 2; i < np && (data[i-1] < data[i]) ; i++) { }
395 else for(i = 2; i < np &&(data[i-1] > data[i]) ; i++) { }
396 
397 if(i != np) throw c2_exception(message);
398 
399 return rev;
400 }
401 
402 template <typename float_type> void
403 c2_function<float_type>::set_sampling_grid(const std::vector<float_type> &grid)
404  throw(c2_exception)
405 {
406 bool rev=this->check_monotonicity(grid,
407 "set_sampling_grid: sampling grid not monotonic");
408 
409 if(!sampling_grid || no_overwrite_grid) sampling_grid=
410 new std::vector<float_type>;
411 (*sampling_grid)=grid; no_overwrite_grid=0;
412 
413 if(rev) std::reverse(sampling_grid->begin(), sampling_grid->end());
414 // make it increasing
415 }
416 
417 template <typename float_type> void c2_function<float_type>::
418 get_sampling_grid(float_type amin, float_type amax,
419 std::vector<float_type> &grid) const
420 {
421 std::vector<float_type> *result=&grid;
422 result->clear();
423 
424 if( !(sampling_grid) || !(sampling_grid->size())
425  || (amax <= sampling_grid->front()) || (amin >= sampling_grid->back()) ) {
426 // nothing is known about the function in this region, return amin and amax
427 result->push_back(amin);
428 result->push_back(amax);
429 } else {
430 std::vector<float_type> &sg=*sampling_grid; // just a shortcut
431 int np=sg.size();
432 int klo=0, khi=np-1, firstindex=0, lastindex=np-1;
433 
434 result->push_back(amin);
435 
436 if(amin > sg.front() ) {
437 // hunt through table for position bracketing starting point
438 while(khi-klo > 1) {
439 int kk=(khi+klo)/2;
440 if(sg[kk] > amin) khi=kk;
441 else klo=kk;
442 }
443 khi=klo+1;
444 // khi now points to first point definitively beyond our first point,
445 // or last point of array
446 firstindex=khi;
447 khi=np-1; // restart upper end of search
448 }
449 
450 if(amax < sg.back()) {
451 // hunt through table for position bracketing starting point
452 while(khi-klo > 1) {
453 int kk=(khi+klo)/2;
454 if(sg[kk] > amax) khi=kk;
455 else klo=kk;
456 }
457 khi=klo+1;
458 // khi now points to first point definitively beyond our last point,
459 // or last point of array
460 lastindex=klo;
461 }
462 
463 int initsize=result->size();
464 result->resize(initsize+(lastindex-firstindex+2));
465 std::copy(sg.begin()+firstindex, sg.begin()+lastindex+1,
466 result->begin()+initsize);
467 result->back()=amax;
468 
469 // this is the unrefined sampling grid... now check for very close
470 // points on front & back and fix if needed.
471 preen_sampling_grid(result);
472 }
473 }
474 
475 template <typename float_type> void
476 c2_function<float_type>::preen_sampling_grid(std::vector<float_type> *result)
477 const
478 {
479 // this is the unrefined sampling grid...
480 // now check for very close points on front & back and fix if needed.
481 if(result->size() > 2) {
482 // may be able to prune dangerously close points near the ends
483 // if there are at least 3 points
484 bool deleteit=false;
485 float_type x0=(*result)[0], x1=(*result)[1];
486 float_type dx1=x1-x0;
487 
488 float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()
489 *(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
490 if(dx1 < ftol) deleteit=true;
491 float_type dx2=(*result)[2]-x0;
492 if(dx1/dx2 < 0.1) deleteit=true;
493 // endpoint is very close to internal interesting point
494 
495 if(deleteit) result->erase(result->begin()+1);
496 // delete redundant interesting point
497 }
498 
499 if(result->size() > 2) {
500 // may be able to prune dangerously close points near the ends
501 // if there are at least 3 points
502 bool deleteit=false;
503 int pos=result->size()-3;
504 float_type x0=(*result)[pos+1], x1=(*result)[pos+2];
505 float_type dx1=x1-x0;
506 
507 float_type ftol=10.0*(std::numeric_limits<float_type>::epsilon()
508 *(std::abs(x0)+std::abs(x1))+std::numeric_limits<float_type>::min());
509 if(dx1 < ftol) deleteit=true;
510 float_type dx2=x1-(*result)[pos];
511 if(dx1/dx2 < 0.1) deleteit=true;
512 // endpoint is very close to internal interesting point
513 
514 if(deleteit) result->erase(result->end()-2);
515 // delete redundant interesting point
516 }
517 }
518 
519 template <typename float_type> void c2_function<float_type>::
520 refine_sampling_grid(std::vector<float_type> &grid, size_t refinement) const
521 {
522 size_t np=grid.size();
523 size_t count=(np-1)*refinement + 1;
524 float_type dxscale=1.0/refinement;
525 
526 std::vector<float_type> result(count);
527 
528 for(size_t i=0; i<(np-1); i++) {
529 float_type x=grid[i];
530 float_type dx=(grid[i+1]-x)*dxscale;
531 for(size_t j=0; j<refinement; j++, x+=dx) result[i*refinement+j]=x;
532 }
533 result.back()=grid.back();
534 grid=result; // copy the expanded grid back to the input
535 }
536 
537 template <typename float_type> float_type
538 c2_function<float_type>::integral(float_type amin, float_type amax,
539 std::vector<float_type> *partials,
540  float_type abs_tol, float_type rel_tol, int derivs,
541 bool adapt, bool extrapolate) const throw(c2_exception)
542 {
543 if(amin==amax) {
544 if(partials) partials->clear();
545 return 0.0;
546 }
547 std::vector<float_type> grid;
548 get_sampling_grid(amin, amax, grid);
549 float_type intg=partial_integrals(grid, partials, abs_tol, rel_tol,
550 derivs, adapt, extrapolate);
551 return intg;
552 }
553 
554 template <typename float_type> c2_function<float_type>
555 &c2_function<float_type>::normalized_function(float_type amin,
556 float_type amax, float_type norm)
557 const throw(c2_exception)
558 {
559 float_type intg=integral(amin, amax);
560 return *new c2_scaled_function_p<float_type>(*this, norm/intg);
561 }
562 
563 template <typename float_type> c2_function<float_type>
564 &c2_function<float_type>::square_normalized_function(float_type amin,
565 float_type amax, float_type norm)
566 const throw(c2_exception)
567 {
568 c2_ptr<float_type> mesquared((*new
569 c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this));
570 
571 std::vector<float_type> grid;
572 get_sampling_grid(amin, amax, grid);
573 float_type intg=mesquared->partial_integrals(grid);
574 
575 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
576 }
577 
578 template <typename float_type> c2_function<float_type>
579 &c2_function<float_type>::square_normalized_function(
580 float_type amin, float_type amax, const c2_function<float_type> &weight,
581 float_type norm)
582 const throw(c2_exception)
583 {
584 c2_ptr<float_type> weighted((*new
585 c2_quadratic_p<float_type>(0., 0., 0., 1.))(*this) * weight);
586 
587 std::vector<float_type> grid;
588 get_sampling_grid(amin, amax, grid);
589 float_type intg=weighted->partial_integrals(grid);
590 
591 return *new c2_scaled_function_p<float_type>(*this, std::sqrt(norm/intg));
592 }
593 
594 template <typename float_type> float_type
595 c2_function<float_type>::partial_integrals(
596 std::vector<float_type> xgrid, std::vector<float_type> *partials,
597 float_type abs_tol, float_type rel_tol, int derivs,
598 bool adapt, bool extrapolate)
599 const throw(c2_exception)
600 {
601 int np=xgrid.size();
602 
603 c2_fblock<float_type> f0, f2;
604 struct c2_integrate_recur rb;
605 rb.rel_tol=rel_tol;
606 rb.extrapolate=extrapolate;
607 rb.adapt=adapt;
608 rb.derivs=derivs;
609 std::vector< recur_item > rb_stack;
610 rb_stack.reserve(20); // enough for most operations
611 rb.rb_stack=&rb_stack;
612 rb.inited=false;
613 float_type dx_inv=1.0/std::abs(xgrid.back()-xgrid.front());
614 
615 if(partials) partials->resize(np-1);
616 
617 float_type sum=0.0;
618 
619 f2.x=xgrid[0];
620 fill_fblock(f2);
621 if(c2_isnan(f2.y)) {
622 bad_x_point=f2.x;
623 return f2.y; // can't go any further if a nan has appeared
624 }
625 
626 for(int i=0; i<np-1; i++) {
627 f0=f2; // copy upper bound to lower before computing new upper bound
628 
629 f2.x=xgrid[i+1];
630 fill_fblock(f2);
631 if(c2_isnan(f2.y)) {
632 bad_x_point=f2.x;
633 return f2.y; // can't go any further if a nan has appeared
634 }
635 
636 rb.abs_tol=abs_tol*std::abs(f2.x-f0.x)*dx_inv;
637 // distribute error tolerance over whole domain
638 rb.f0=&f0; rb.f1=&f2;
639 float_type psss=integrate_step(rb);
640 sum+=psss;
641 if(partials) (*partials)[i]=psss;
642 if(c2_isnan(psss)) break; // NaN stops integration
643 }
644 return sum;
645 }
646 
647 // generate a sampling grid at points separated by dx=5, which is intentionally
648 // incommensurate with pi and 2*pi so grid errors are somewhat randomized
649 template <typename float_type> void c2_sin_p<float_type>::
650 get_sampling_grid(float_type amin, float_type amax,
651 std::vector<float_type> &grid) const
652 {
653 grid.clear();
654 for(; amin < amax; amin+=5.0) grid.push_back(amin);
655 grid.push_back(amax);
656 this->preen_sampling_grid(&grid);
657 }
658 
659 template <typename float_type> float_type
660 c2_function_transformation<float_type>::evaluate(
661 float_type xraw,
662 float_type y, float_type yp0, float_type ypp0,
663 float_type *yprime, float_type *yprime2) const
664 {
665 y=Y.fHasStaticTransforms ? Y.pOut(y) : Y.fOut(y);
666 
667 if(yprime || yprime2) {
668 
669 float_type yp, yp2;
670 if(X.fHasStaticTransforms && Y.fHasStaticTransforms) {
671 float_type fpi=1.0/Y.pInPrime(y);
672 float_type gp=X.pInPrime(xraw);
673 // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
674 yp=gp*yp0*fpi; // transformed derivative
675 yp2=(gp*gp*ypp0 + X.pInDPrime(xraw)*yp0 - Y.pInDPrime(y)*yp*yp )*fpi;
676 } else {
677 float_type fpi=1.0/Y.fInPrime(y);
678 float_type gp=X.fInPrime(xraw);
679 // from Mathematica Dt[InverseFunction[f][y[g[x]]], x]
680 yp=gp*yp0*fpi; // transformed derivative
681 yp2=(gp*gp*ypp0 + X.fInDPrime(xraw)*yp0 - Y.fInDPrime(y)*yp*yp )*fpi;
682 }
683 if(yprime) *yprime=yp;
684 if(yprime2) *yprime2=yp2;
685 }
686 return y;
687 }
688 
689 // The constructor
690 template <typename float_type> interpolating_function_p<float_type>
691 &interpolating_function_p<float_type>::load(
692 const std::vector<float_type> &x, const std::vector<float_type> &f,
693 bool lowerSlopeNatural, float_type lowerSlope,
694 bool upperSlopeNatural, float_type upperSlope,
695 bool splined
696 ) throw(c2_exception)
697 {
698 c2_ptr<float_type> keepme(*this);
699 X= x;
700 F= f;
701 
702 Xraw=x;
703 
704 this->set_domain(std::min(Xraw.front(), Xraw.back()),
705 std::max(Xraw.front(), Xraw.back()));
706 
707 if(x.size() != f.size()) {
708 throw c2_exception(
709 "interpolating_function::init() -- x & y inputs are of different size");
710 }
711 
712 size_t np=X.size(); // they are the same now, so lets take a short cut
713 
714 if(np < 2) {
715 throw c2_exception("interpolating_function::init() -- input < 2 elements ");
716 }
717 
718 bool xraw_rev=this->check_monotonicity(Xraw,
719 "interpolating_function::init() non-monotonic raw x input");
720 // which way does raw X point? sampling grid MUST be increasing
721 
722 if(!xraw_rev) {
723 // we can use pointer to raw X values if they are in the right order
724 this->set_sampling_grid_pointer(Xraw);
725 // our intial grid of x values is certainly a good guess for points
726 } else {
727 this->set_sampling_grid(Xraw);
728 // make a copy of it, and assure it is in right order
729 }
730 
731 if(fTransform.X.fTransformed) {
732 // check if X scale is nonlinear, and if so, do transform
733 if(!lowerSlopeNatural) lowerSlope /= fTransform.X.fInPrime(X[0]);
734 if(!upperSlopeNatural) upperSlope /= fTransform.X.fInPrime(X[np-1]);
735 for(size_t i=0; i<np; i++) X[i]=fTransform.X.fIn(X[i]);
736 }
737 if(fTransform.Y.fTransformed) {
738 // check if Y scale is nonlinear, and if so, do transform
739 if(!lowerSlopeNatural) lowerSlope *= fTransform.Y.fInPrime(F[0]);
740 if(!upperSlopeNatural) upperSlope *= fTransform.Y.fInPrime(F[np-1]);
741 for(size_t i=0; i<np; i++) F[i]=fTransform.Y.fIn(F[i]);
742 }
743 
744 xInverted=this->check_monotonicity(X,
745 "interpolating_function::init() non-monotonic transformed x input");
746 
747 if(splined) spline(lowerSlopeNatural, lowerSlope,
748 upperSlopeNatural, upperSlope);
749 else y2.assign(np,0.0);
750 
751 lastKLow=0;
752 keepme.release_for_return();
753 return *this;
754 }
755 
756 
757 // The constructor
758 template <typename float_type> interpolating_function_p<float_type>
759 &interpolating_function_p<float_type>::load_pairs(
760 std::vector<std::pair<float_type, float_type> > &data,
761 bool lowerSlopeNatural, float_type lowerSlope,
762 bool upperSlopeNatural, float_type upperSlope,
763 bool splined
764 ) throw(c2_exception)
765 {
766 c2_ptr<float_type> keepme(*this);
767 
768 size_t np=data.size();
769 if(np < 2) {
770 throw c2_exception("interpolating_function::init() -- input < 2 elements ");
771 }
772 
773 // sort into ascending order
774 std::sort(data.begin(), data.end(), comp_pair);
775 
776 std::vector<float_type> xtmp, ytmp;
777 xtmp.reserve(np);
778 ytmp.reserve(np);
779 for (size_t i=0; i<np; i++) {
780 xtmp.push_back(data[i].first);
781 ytmp.push_back(data[i].second);
782 }
783 this->load(xtmp, ytmp, lowerSlopeNatural, lowerSlope,
784 upperSlopeNatural, upperSlope, splined);
785 
786 keepme.release_for_return();
787 return *this;
788 }
789 
790 template <typename float_type> interpolating_function_p<float_type> &
791 interpolating_function_p<float_type>::load_random_generator_function(
792 const std::vector<float_type> &bincenters,
793 const c2_function<float_type> &binheights)
794 throw(c2_exception)
795 {
796 c2_ptr<float_type> keepme(*this);
797 
798 std::vector<float_type> integral;
799 c2_const_ptr<float_type> keepit(binheights);
800 // integrate from first to last bin in original order,
801 // leaving results in integral
802 // ask for relative error of 1e-6 on each bin, with absolute
803 // error set to 0 (since we don't know the data scale).
804 float_type sum=binheights.partial_integrals(bincenters, &integral, 0.0, 1e-6);
805 integral.insert(integral.begin(), 0.0); // integral from start to start is 0
806 float_type scale=1.0/sum;
807 for(size_t i=1; i<integral.size(); i++) integral[i]=integral[i]*scale
808 + integral[i-1];
809 integral.back()=1.0; // force exact value on boundary
810 
811 this->load(integral, bincenters,
812  false, 1.0/(scale*binheights(bincenters.front() )),
813  false, 1.0/(scale*binheights(bincenters.back() ))
814  ); // use integral as x axis in inverse function
815 keepme.release_for_return();
816 return *this;
817 }
818 
819 template <typename float_type> interpolating_function_p<float_type> &
820 interpolating_function_p<float_type>::load_random_generator_bins(
821 const std::vector<float_type> &bins,
822 const std::vector<float_type> &binheights, bool splined)
823 throw(c2_exception)
824 {
825 c2_ptr<float_type> keepme(*this);
826 
827 size_t np=binheights.size();
828 std::vector<float_type> integral(np+1), bin_edges(np+1);
829 
830 // compute the integral based on estimates of the bin edges
831 // from the given bin centers...
832 // except for bin 0 & final bin, the edge of a bin is halfway
833 // between then center of the
834 // bin and the center of the previous/next bin.
835 
836 if(bins.size() == binheights.size()+1) {
837 bin_edges=bins; // edges array was passed in
838 } else if (bins.size() == binheights.size()) {
839 bin_edges.front()=bins[0] - (bins[1]-bins[0])*0.5; // edge bin
840 for(size_t i=1; i<np; i++) {
841 bin_edges[i]=(bins[i]+bins[i-1])*0.5;
842 }
843 bin_edges.back()=bins[np-1] + (bins[np-1]-bins[np-2])*0.5; // edge bin
844 } else {
845 throw c2_exception(
846 "inconsistent bin vectors passed to load_random_generator_bins");
847 }
848 
849 float_type running_sum=0.0;
850 for(size_t i=0; i<np; i++) {
851 integral[i]=running_sum;
852 if(!binheights[i]) throw c2_exception(
853 "empty bin passed to load_random_generator_bins");
854 running_sum+=binheights[i]*std::abs(bin_edges[i+1]-bin_edges[i]);
855 }
856 float_type scale=1.0/running_sum;
857 for(size_t i=0; i<np; i++) integral[i]*=scale;
858 integral.back()=1.0; // force exactly correct value on boundary
859 this->load(integral, bin_edges,
860  false, 1.0/(scale*binheights.front()),
861  false, 1.0/(scale*binheights.back()),
862  splined); // use integral as x axis in inverse function
863 keepme.release_for_return();
864 return *this;
865 }
866 
867 
868 // The spline table generator
869 template <typename float_type> void
870 interpolating_function_p<float_type>::spline(
871  bool lowerSlopeNatural, float_type lowerSlope,
872  bool upperSlopeNatural, float_type upperSlope
873  ) throw(c2_exception)
874 {
875 // construct spline tables here.
876 // this code is a re-translation of the pythonlabtools spline
877 // algorithm from pythonlabtools.sourceforge.net
878 size_t np=X.size();
879 std::vector<float_type> u(np), dy(np-1), dx(np-1),
880 dxi(np-1), dx2i(np-2), siga(np-2), dydx(np-1);
881 
882 std::transform(X.begin()+1, X.end(), X.begin(), dx.begin(),
883 std::minus<float_type>() ); // dx=X[1:] - X [:-1]
884 for(size_t i=0; i<dxi.size(); i++) dxi[i]=1.0/dx[i]; // dxi = 1/dx
885 for(size_t i=0; i<dx2i.size(); i++) dx2i[i]=1.0/(X[i+2]-X[i]);
886 
887 std::transform(F.begin()+1, F.end(), F.begin(), dy.begin(),
888 std::minus<float_type>() ); // dy = F[i+1]-F[i]
889 std::transform(dx2i.begin(), dx2i.end(), dx.begin(), siga.begin(),
890 std::multiplies<float_type>()); // siga = dx[:-1]*dx2i
891 std::transform(dxi.begin(), dxi.end(), dy.begin(), dydx.begin(),
892 std::multiplies<float_type>()); // dydx=dy/dx
893 
894 // u[i]=(y[i+1]-y[i])/float(x[i+1]-x[i]) - (y[i]-y[i-1])/float(x[i]-x[i-1])
895 std::transform(dydx.begin()+1, dydx.end(), dydx.begin(), u.begin()+1,
896 std::minus<float_type>() ); // incomplete rendering of u = dydx[1:]-dydx[:-1]
897 
898 y2.resize(np,0.0);
899 
900 if(lowerSlopeNatural) {
901 y2[0]=u[0]=0.0;
902 } else {
903 y2[0]= -0.5;
904 u[0]=(3.0*dxi[0])*(dy[0]*dxi[0] -lowerSlope);
905 }
906 
907 for(size_t i=1; i < np -1; i++) { // the inner loop
908 float_type sig=siga[i-1];
909 float_type p=sig*y2[i-1]+2.0;
910 y2[i]=(sig-1.0)/p;
911 u[i]=(6.0*u[i]*dx2i[i-1] - sig*u[i-1])/p;
912 }
913 
914 float_type qn, un;
915 
916 if(upperSlopeNatural) {
917 qn=un=0.0;
918 } else {
919 qn= 0.5;
920 un=(3.0*dxi[dxi.size()-1])*(upperSlope- dy[dy.size()-1]*dxi[dxi.size()-1] );
921 }
922 
923 y2[np-1]=(un-qn*u[np-2])/(qn*y2[np-2]+1.0);
924 for (size_t k=np-1; k != 0; k--) y2[k-1]=y2[k-1]*y2[k]+u[k-1];
925 }
926 
927 template <typename float_type> interpolating_function_p<float_type>
928 &interpolating_function_p<float_type>::sample_function(
929 const c2_function<float_type> &func,
930 float_type amin, float_type amax, float_type abs_tol, float_type rel_tol,
931 bool lowerSlopeNatural, float_type lowerSlope,
932 bool upperSlopeNatural, float_type upperSlope
933  ) throw(c2_exception)
934 {
935 c2_ptr<float_type> keepme(*this);
936 
937 const c2_transformation<float_type> &XX=fTransform.X, &YY=fTransform.Y;
938 
939  // set up our params to look like the samplng function for now
940  sampler_function=func;
941  std::vector<float_type> grid;
942  func.get_sampling_grid(amin, amax, grid);
943  size_t gsize=grid.size();
944  if(XX.fTransformed) for(size_t i=0; i<gsize; i++) grid[i]=XX.fIn(grid[i]);
945  this->set_sampling_grid_pointer(grid);
946 
947 this->adaptively_sample(grid.front(), grid.back(), 8*abs_tol,
948 8*rel_tol, 0, &X, &F);
949 // clear the sampler function now, since otherwise our
950 // value_with_derivatives is broken
951  sampler_function.unset_function();
952 
953  xInverted=this->check_monotonicity(X,
954  "interpolating_function::init() non-monotonic transformed x input");
955 
956  size_t np=X.size();
957 
958  // Xraw is useful in some of the arithmetic operations between
959 // interpolating functions
960  if(!XX.fTransformed) Xraw=X;
961  else {
962  Xraw.resize(np);
963  for (size_t i=1; i<np-1; i++) Xraw[i]=XX.fOut(X[i]);
964  Xraw.front()=amin;
965  Xraw.back()=amax;
966  }
967 
968  bool xraw_rev=this->check_monotonicity(Xraw,
969  "interpolating_function::init() non-monotonic raw x input");
970  // which way does raw X point? sampling grid MUST be increasing
971 
972  if(!xraw_rev) {
973  this->set_sampling_grid_pointer(Xraw);
974 
975  } else {
976  this->set_sampling_grid(Xraw);
977  }
978 
979  if(XX.fTransformed) { // check if X scale is nonlinear, and if so, do transform
980  if(!lowerSlopeNatural) lowerSlope /= XX.fInPrime(amin);
981  if(!upperSlopeNatural) upperSlope /= XX.fInPrime(amax);
982  }
983  if(YY.fTransformed) {
984  if(!lowerSlopeNatural) lowerSlope *= YY.fInPrime(func(amin));
985  if(!upperSlopeNatural) upperSlope *= YY.fInPrime(func(amax));
986  }
987  // note that each of ends has 3 points with two equal gaps,
988 // since they were obtained by bisection
989  // so the step sizes are easy to get
990  // the 'natural slope' option for sampled functions has a
991 // different meaning than
992  // for normal splines. In this case, the derivative is adjusted to make the
993  // second derivative constant on the last two points at each end
994  // which is consistent with the error sampling technique we used to get here
995  if(lowerSlopeNatural) {
996  float_type hlower=X[1]-X[0];
997  lowerSlope=0.5*(-F[2]-3*F[0]+4*F[1])/hlower;
998  lowerSlopeNatural=false; // it's not the usual meaning of natural any more
999  }
1000  if(upperSlopeNatural) {
1001  float_type hupper=X[np-1]-X[np-2];
1002  upperSlope=0.5*(F[np-3]+3*F[np-1]-4*F[np-2])/hupper;
1003  upperSlopeNatural=false; // it's not the usual meaning of natural any more
1004  }
1005  this->set_domain(amin, amax);
1006 
1007  spline(lowerSlopeNatural, lowerSlope, upperSlopeNatural, upperSlope);
1008  lastKLow=0;
1009  keepme.release_for_return();
1010  return *this;
1011 }
1012 
1013 // This function is the reason for this class to exist
1014 // it computes the interpolated function, and (if requested) its proper
1015 // first and second derivatives including all coordinate transforms
1016 template <typename float_type> float_type
1017 interpolating_function_p<float_type>::value_with_derivatives(
1018 float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception)
1019 {
1020 if(sampler_function.valid()) {
1021 // if this is non-null, we are sampling data for later,
1022 // so just return raw function
1023 // however, transform it into our sampling space, first.
1024 if(yprime) *yprime=0;
1025 if(yprime2) *yprime2=0;
1026 sampler_function->increment_evaluations();
1027 return fTransform.Y.fIn(sampler_function(fTransform.X.fOut(x)));
1028 // derivatives are completely undefined
1029 }
1030 
1031 if(x < this->xmin() || x > this->xmax()) {
1032 std::ostringstream outstr;
1033 outstr << "Interpolating function argument " << x
1034 << " out of range " << this->xmin() << " -- " << this ->xmax() << ": bailing";
1035 throw c2_exception(outstr.str().c_str());
1036  }
1037 
1038 float_type xraw=x;
1039 
1040 if(fTransform.X.fTransformed) x=fTransform.X.fHasStaticTransforms?
1041 fTransform.X.pIn(x) : fTransform.X.fIn(x);
1042 // save time by explicitly testing for identity function here
1043 
1044 int klo=0, khi=X.size()-1;
1045 
1046 if(khi < 0) throw c2_exception(
1047 "Uninitialized interpolating function being evaluated");
1048 
1049 const float_type *XX=&X[lastKLow];
1050 // make all fast checks short offsets from here
1051 
1052 if(!xInverted) {
1053 // select search depending on whether transformed X is increasing or decreasing
1054 if((XX[0] <= x) && (XX[1] >= x) ) { // already bracketed
1055 klo=lastKLow;
1056 } else if((XX[1] <= x) && (XX[2] >= x)) { // in next bracket to the right
1057 klo=lastKLow+1;
1058 } else if(lastKLow > 0 && (XX[-1] <= x) && (XX[0] >= x)) {
1059 // in next bracket to the left
1060 klo=lastKLow-1;
1061 } else { // not bracketed, not close, start over
1062  // search for new KLow
1063 while(khi-klo > 1) {
1064 int kk=(khi+klo)/2;
1065 if(X[kk] > x) khi=kk;
1066 else klo=kk;
1067 }
1068 }
1069 } else {
1070 if((XX[0] >= x) && (XX[1] <= x) ) { // already bracketed
1071 klo=lastKLow;
1072 } else if((XX[1] >= x) && (XX[2] <= x)) { // in next bracket to the right
1073 klo=lastKLow+1;
1074 } else if(lastKLow > 0 && (XX[-1] >= x) && (XX[0] <= x)) {
1075 // in next bracket to the left
1076 klo=lastKLow-1;
1077 } else { // not bracketed, not close, start over
1078  // search for new KLow
1079 while(khi-klo > 1) {
1080 int kk=(khi+klo)/2;
1081 if(X[kk] < x) khi=kk;
1082 else klo=kk;
1083 }
1084 }
1085 }
1086 
1087 khi=klo+1;
1088 lastKLow=klo;
1089 
1090 float_type h=X[khi]-X[klo];
1091 
1092 float_type a=(X[khi]-x)/h;
1093 float_type b=1.0-a;
1094 float_type ylo=F[klo], yhi=F[khi], y2lo=y2[klo], y2hi=y2[khi];
1095 float_type y=a*ylo+b*yhi+((a*a*a-a)*y2lo+(b*b*b-b)*y2hi)*(h*h)/6.0;
1096 
1097 float_type yp0=0; // the derivative in interpolating table coordinates
1098 float_type ypp0=0; // second derivative
1099 
1100 if(yprime || yprime2) {
1101 yp0=(yhi-ylo)/h+((3*b*b-1)*y2hi-(3*a*a-1)*y2lo)*h/6.0;
1102 // the derivative in interpolating table coordinates
1103 ypp0=b*y2hi+a*y2lo; // second derivative
1104 }
1105 
1106 if(fTransform.isIdentity) {
1107 if(yprime) *yprime=yp0;
1108 if(yprime2) *yprime2=ypp0;
1109 return y;
1110 } else return fTransform.evaluate(xraw, y, yp0, ypp0, yprime, yprime2);
1111 }
1112 
1113 template <typename float_type> void
1114 interpolating_function_p<float_type>::set_lower_extrapolation(float_type bound)
1115 {
1116 int kl = 0 ;
1117 int kh=kl+1;
1118 float_type xx=fTransform.X.fIn(bound);
1119 float_type h0=X[kh]-X[kl];
1120 float_type h1=xx-X[kl];
1121 float_type yextrap=F[kl]+((F[kh]-F[kl])/h0
1122 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
1123 
1124 X.insert(X.begin(), xx);
1125 F.insert(F.begin(), yextrap);
1126 y2.insert(y2.begin(), y2.front()); // duplicate first or last element
1127 Xraw.insert(Xraw.begin(), bound);
1128 if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
1129 else this->fXMax=bound;
1130 
1131 }
1132 
1133 template <typename float_type> void
1134 interpolating_function_p<float_type>::set_upper_extrapolation(float_type bound)
1135 {
1136 int kl = X.size()-2 ;
1137 int kh=kl+1;
1138 float_type xx=fTransform.X.fIn(bound);
1139 float_type h0=X[kh]-X[kl];
1140 float_type h1=xx-X[kl];
1141 float_type yextrap=F[kl]+((F[kh]-F[kl])/h0
1142 - h0*(y2[kl]+2.0*y2[kh])/6.0)*h1+y2[kl]*h1*h1/2.0;
1143 
1144 X.insert(X.end(), xx);
1145 F.insert(F.end(), yextrap);
1146 y2.insert(y2.end(), y2.back()); // duplicate first or last element
1147 Xraw.insert(Xraw.end(), bound);
1148 if (bound < this->fXMin) this->fXMin=bound; // check for reversed data
1149 else this->fXMax=bound;
1150 //printf("%10.4f %10.4f %10.4f %10.4f %10.4f\n", bound, xx, h0, h1, yextrap);
1151 
1152 }
1153 
1154 template <typename float_type> interpolating_function_p<float_type>&
1155 interpolating_function_p<float_type>::unary_operator(
1156 const c2_function<float_type> &source) const
1157 {
1158 size_t np=X.size();
1159 std::vector<float_type>yv(np);
1160 c2_ptr<float_type> comp(source(*this));
1161 float_type yp0, yp1, ypp;
1162 
1163 for(size_t i=1; i<np-1; i++) {
1164 yv[i]=source(fTransform.Y.fOut(F[i]));
1165 }
1166 
1167 yv.front()=comp(Xraw.front(), &yp0, &ypp); // get derivative at front
1168 yv.back()= comp(Xraw.back(), &yp1, &ypp); // get derivative at back
1169 
1170 interpolating_function_p &copy=clone();
1171 copy.load(this->Xraw, yv, false, yp0, false, yp1);
1172 
1173 return copy;
1174 }
1175 
1176 template <typename float_type> void
1177 interpolating_function_p<float_type>::get_data(std::vector<float_type> &xvals,
1178 std::vector<float_type> &yvals) const throw()
1179 {
1180 
1181 xvals=Xraw;
1182 yvals.resize(F.size());
1183 
1184 for(size_t i=0; i<F.size(); i++) yvals[i]=fTransform.Y.fOut(F[i]);
1185 }
1186 
1187 template <typename float_type> interpolating_function_p<float_type> &
1188 interpolating_function_p<float_type>::binary_operator(const
1189 c2_function<float_type> &rhs,
1190 const c2_binary_function<float_type> *combining_stub) const
1191 {
1192 size_t np=X.size();
1193 std::vector<float_type> yv(np);
1194 c2_constant_p<float_type> fval(0);
1195 float_type yp0, yp1, ypp;
1196 
1197 c2_const_ptr<float_type> stub(*combining_stub); // manage ownership
1198 
1199 for(size_t i=1; i<np-1; i++) {
1200 fval.reset(fTransform.Y.fOut(F[i])); // update the constant function pointwise
1201 yv[i]=combining_stub->combine(fval, rhs, Xraw[i], (float_type *)0,
1202 (float_type *)0); // compute rhs & combine without derivatives
1203 }
1204 
1205 yv.front()=combining_stub->combine(*this, rhs, Xraw.front(), &yp0, &ypp);
1206 yv.back()= combining_stub->combine(*this, rhs, Xraw.back(), &yp1, &ypp);
1207 
1208 interpolating_function_p &copy=clone();
1209 copy.load(this->Xraw, yv, false, yp0, false, yp1);
1210 
1211 return copy;
1212 }
1213 
1214 template <typename float_type>
1215 c2_inverse_function_p<float_type>::c2_inverse_function_p(
1216 const c2_function<float_type> &source)
1217 : c2_function<float_type>(), func(source)
1218 {
1219 float_type l=source.xmin();
1220 float_type r=source.xmax();
1221 start_hint=(l+r)*0.5; // guess that we start in the middle
1222 
1223 float_type ly=source(l);
1224 float_type ry=source(r);
1225 if (ly > ry) {
1226 float_type t=ly; ly=ry; ry=t;
1227 }
1228 this->set_domain(ly, ry);
1229 }
1230 
1231 template <typename float_type> float_type
1232 c2_inverse_function_p<float_type>::value_with_derivatives(
1233 float_type x, float_type *yprime, float_type *yprime2
1234 ) const throw(c2_exception)
1235 {
1236 float_type l=this->func->xmin();
1237 float_type r=this->func->xmax();
1238 float_type yp, ypp;
1239 float_type y=this->func->find_root(l, r, get_start_hint(x), x, 0, &yp, &ypp);
1240 start_hint=y;
1241 if(yprime) *yprime=1.0/yp;
1242 if(yprime2) *yprime2=-ypp/(yp*yp*yp);
1243 return y;
1244 }
1245 
1246 template <typename float_type>
1247 accumulated_histogram<float_type>::accumulated_histogram(
1248 const std::vector<float_type>binedges, const std::vector<float_type> binheights,
1249 bool normalize, bool inverse_function, bool drop_zeros)
1250 {
1251 
1252 int np=binheights.size();
1253 
1254 std::vector<float_type> be, bh;
1255 if(drop_zeros || inverse_function) {
1256 if(binheights[0] || !inverse_function) {
1257 be.push_back(binedges[0]);
1258 bh.push_back(binheights[0]);
1259 }
1260 for(int i=1; i<np-1; i++) {
1261 if(binheights[i]) {
1262 be.push_back(binedges[i]);
1263 bh.push_back(binheights[i]);
1264 }
1265 }
1266 if(binheights[np-1] || !inverse_function) {
1267 bh.push_back(binheights[np-1]);
1268 be.push_back(binedges[np-1]);
1269 be.push_back(binedges[np]); // push both sides of the last bin if needed
1270 }
1271 np=bh.size(); // set np to compressed size of bin array
1272 } else {
1273 be=binedges;
1274 bh=binheights;
1275 }
1276 std::vector<float_type> cum(np+1, 0.0);
1277 for(int i=1; i<=np; i++) cum[i]=bh[i]*(be[i]-be[i-1])+cum[i-1];
1278 // accumulate bins, leaving bin 0 as 0
1279 if(be[1] < be[0]) { // if bins passed in backwards, reverse them
1280 std::reverse(be.begin(), be.end());
1281 std::reverse(cum.begin(), cum.end());
1282 for(unsigned int i=0; i<cum.size(); i++) cum[i]*=-1;
1283 // flip sign on reversed data
1284 }
1285 if(normalize) {
1286 float_type mmm=1.0/std::max(cum[0], cum[np]);
1287 for(int i=0; i<=np; i++) cum[i]*=mmm;
1288 }
1289 if(inverse_function) interpolating_function_p<float_type>(cum, be);
1290 // use cum as x axis in inverse function
1291 else interpolating_function_p<float_type>(be, cum);
1292 // else use lower bin edge as x axis
1293 std::fill(this->y2.begin(), this->y2.end(), 0.0);
1294 // clear second derivatives, to we are piecewise linear
1295 }
1296 
1297 template <typename float_type>
1298 c2_piecewise_function_p<float_type>::c2_piecewise_function_p()
1299 : c2_function<float_type>(), lastKLow(-1)
1300 {
1301 this->sampling_grid=new std::vector<float_type>;
1302 }
1303 
1304 template <typename float_type>
1305 c2_piecewise_function_p<float_type>::~c2_piecewise_function_p()
1306 {
1307 }
1308 
1309 template <typename float_type> float_type
1310 c2_piecewise_function_p<float_type>::value_with_derivatives(
1311  float_type x, float_type *yprime, float_type *yprime2
1312  ) const throw(c2_exception)
1313 {
1314 
1315 size_t np=functions.size();
1316 if(!np) throw c2_exception(
1317 "attempting to evaluate an empty piecewise function");
1318 
1319 if(x < this->xmin() || x > this->xmax()) {
1320 std::ostringstream outstr;
1321 outstr << "piecewise function argument " << x << " out of range "
1322 << this->xmin() << " -- " << this->xmax();
1323 throw c2_exception(outstr.str().c_str());
1324 }
1325 
1326 int klo=0;
1327 
1328 if(lastKLow >= 0 && functions[lastKLow]->xmin() <= x
1329 && functions[lastKLow]->xmax() > x) {
1330 klo=lastKLow;
1331 } else {
1332 int khi=np;
1333 while(khi-klo > 1) {
1334 int kk=(khi+klo)/2;
1335 if(functions[kk]->xmin() > x) khi=kk;
1336 else klo=kk;
1337 }
1338 }
1339 lastKLow=klo;
1340 return functions[klo]->value_with_derivatives(x, yprime, yprime2);
1341 }
1342 
1343 template <typename float_type> void
1344 c2_piecewise_function_p<float_type>::append_function(
1345 const c2_function<float_type> &func) throw(c2_exception)
1346 {
1347 c2_const_ptr<float_type> keepfunc(func);
1348 if(functions.size()) { // check whether there are any gaps to fill, etc.
1349 const c2_function<float_type> &tail=functions.back();
1350 float_type x0=tail.xmax();
1351 float_type x1=func.xmin();
1352 if(x0 < x1) {
1353 // must insert a connector if x0 < x1
1354 float_type y0=tail(x0);
1355 float_type y1=func(x1);
1356 c2_function<float_type> &connector=
1357 *new c2_linear_p<float_type>(x0, y0, (y1-y0)/(x1-x0));
1358 connector.set_domain(x0,x1);
1359 functions.push_back(c2_const_ptr<float_type>(connector));
1360 this->sampling_grid->push_back(x1);
1361 } else if(x0>x1) throw c2_exception(
1362 "function domains not increasing in c2_piecewise_function");
1363 }
1364 functions.push_back(keepfunc);
1365 // extend our domain to include all known functions
1366 this->set_domain(functions.front()->xmin(), functions.back()->xmax());
1367 // extend our sampling grid with the new function's grid,
1368 // with the first point dropped to avoid duplicates
1369 std::vector<float_type> newgrid;
1370 func.get_sampling_grid(func.xmin(), func.xmax(), newgrid);
1371 this->sampling_grid->insert(this->sampling_grid->end(),
1372 newgrid.begin()+1, newgrid.end());
1373 }
1374 
1375 template <typename float_type>
1376 c2_connector_function_p<float_type>::c2_connector_function_p(
1377 float_type x0, const c2_function<float_type> &f0, float_type x2,
1378 const c2_function<float_type> &f2,
1379 bool auto_center, float_type y1)
1380 : c2_function<float_type>()
1381 {
1382 c2_const_ptr<float_type> left(f0), right(f2);
1383 c2_fblock<float_type> fb0, fb2;
1384 fb0.x=x0;
1385 f0.fill_fblock(fb0);
1386 fb2.x=x2;
1387 f2.fill_fblock(fb2);
1388 init(fb0, fb2, auto_center, y1);
1389 }
1390 
1391 template <typename float_type>
1392 c2_connector_function_p<float_type>::c2_connector_function_p(
1393 float_type x0, float_type y0, float_type yp0, float_type ypp0,
1394 float_type x2, float_type y2, float_type yp2, float_type ypp2,
1395 bool auto_center, float_type y1)
1396 : c2_function<float_type>()
1397 {
1398 c2_fblock<float_type> fb0, fb2;
1399 fb0.x=x0; fb0.y=y0; fb0.yp=yp0; fb0.ypp=ypp0;
1400 fb2.x=x2; fb2.y=y2; fb2.yp=yp2; fb2.ypp=ypp2;
1401 init(fb0, fb2, auto_center, y1);
1402 }
1403 
1404 template <typename float_type>
1405 c2_connector_function_p<float_type>::c2_connector_function_p(
1406 const c2_fblock<float_type> &fb0,
1407 const c2_fblock<float_type> &fb2,
1408 bool auto_center, float_type y1)
1409 : c2_function<float_type>()
1410 {
1411 init(fb0, fb2, auto_center, y1);
1412 }
1413 
1414 template <typename float_type> void c2_connector_function_p<float_type>::init(
1415 const c2_fblock<float_type> &fb0,
1416 const c2_fblock<float_type> &fb2,
1417 bool auto_center, float_type y1)
1418 {
1419 float_type dx=(fb2.x-fb0.x)/2.0;
1420 fhinv=1.0/dx;
1421 
1422 // scale derivs to put function on [-1,1] since mma solution is done this way
1423 float_type yp0=fb0.yp*dx;
1424 float_type yp2=fb2.yp*dx;
1425 float_type ypp0=fb0.ypp*dx*dx;
1426 float_type ypp2=fb2.ypp*dx*dx;
1427 
1428 float_type ff0=(8*(fb0.y + fb2.y) + 5*(yp0 - yp2) + ypp0 + ypp2)*0.0625;
1429 if(auto_center) y1=ff0; // forces ff to be 0 if we are auto-centering
1430 
1431 // y[x_] = y1 + x (a + b x) + x [(x-1) (x+1)] (c + d x)
1432 // + x (x-1)^2 (x+1)^2 (e + f x)
1433 // y' = a + 2 b x + d x [(x+1)(x-1)] + (c + d x)(3x^2-1)
1434 // + f x [(x+1)(x-1)]^2 + (e + f x)[(x+1)(x-1)](5x^2-1)
1435 // y'' = 2 b + 6x(c + d x) + 2d(3x^2-1) + 4x(e + f x)(5x^2-3)
1436 // + 2f(x^2-1)(5x^2-1)
1437 fy1=y1;
1438 fa=(fb2.y - fb0.y)*0.5;
1439 fb=(fb0.y + fb2.y)*0.5 - y1;
1440 fc=(yp2+yp0-2.*fa)*0.25;
1441 fd=(yp2-yp0-4.*fb)*0.25;
1442 fe=(ypp2-ypp0-12.*fc)*0.0625;
1443 ff=(ff0 - y1);
1444 this->set_domain(fb0.x, fb2.x); // this is where the function is valid
1445 }
1446 
1447 template <typename float_type>
1448 c2_connector_function_p<float_type>::~c2_connector_function_p()
1449 {
1450 }
1451 
1452 template <typename float_type> float_type
1453 c2_connector_function_p<float_type>::value_with_derivatives(
1454 float_type x, float_type *yprime, float_type *yprime2
1455 ) const throw(c2_exception)
1456 {
1457 float_type x0=this->xmin(), x2=this->xmax();
1458 float_type dx=(x-(x0+x2)*0.5)*fhinv;
1459 float_type q1=(x-x0)*(x-x2)*fhinv*fhinv;
1460 float_type q2=dx*q1;
1461 
1462 float_type r1=fa+fb*dx;
1463 float_type r2=fc+fd*dx;
1464 float_type r3=fe+ff*dx;
1465 
1466 float_type y=fy1+dx*r1+q2*r2+q1*q2*r3;
1467 
1468 if(yprime || yprime2) {
1469 float_type q3=3*q1+2;
1470 float_type q4=5*q1+4;
1471 if(yprime) *yprime=(fa+2*fb*dx+fd*q2+r2*q3+ff*q1*q2+q1*q4*r3)*fhinv;
1472 if(yprime2) *yprime2=2*(fb+fd*q3+3*dx*r2+ff*q1*q4
1473 +r3*(2*dx*(5*q1+2)))*fhinv*fhinv;
1474 }
1475 return y;
1476 }
1477 
1478 // the recursive part of the sampler is agressively designed
1479 // to minimize copying of data... lots of pointers
1480 template <typename float_type> void
1481 c2_function<float_type>::sample_step(c2_sample_recur &rb) const
1482 throw(c2_exception)
1483 {
1484 std::vector< recur_item > &rb_stack=*rb.rb_stack;
1485 // heap-based stack of data for recursion
1486 rb_stack.clear();
1487 
1488 recur_item top;
1489 top.depth=0; top.done=false; top.f0index=0; top.f2index=0;
1490 
1491 // push storage for our initial elements
1492 rb_stack.push_back(top);
1493 rb_stack.back().f1=*rb.f0;
1494 rb_stack.back().done=true;
1495 
1496 rb_stack.push_back(top);
1497 rb_stack.back().f1=*rb.f1;
1498 rb_stack.back().done=true;
1499 
1500 if(!rb.inited) {
1501 rb.dx_tolerance=10.0*std::numeric_limits<float_type>::epsilon();
1502 rb.abs_tol_min=10.0*std::numeric_limits<float_type>::min();
1503 rb.inited=true;
1504 }
1505 
1506 // now, push our first real element
1507 top.f0index=0; // left element is stack[0]
1508 top.f2index=1; // right element is stack[1]
1509 rb_stack.push_back(top);
1510 
1511 while(rb_stack.size() > 2) {
1512 recur_item &back=rb_stack.back();
1513 if(back.done) {
1514 rb_stack.pop_back();
1515 continue;
1516 }
1517 back.done=true;
1518 
1519 c2_fblock<float_type> &f0=rb_stack[back.f0index].f1,
1520 &f2=rb_stack[back.f2index].f1;
1521 c2_fblock<float_type> &f1=back.f1; // will hold new middle values
1522 size_t f1index=rb_stack.size()-1; // our current offset
1523 
1524 
1525 f1.x=0.5*(f0.x + f2.x); // center of interval
1526 float_type dx2=0.5*(f2.x - f0.x);
1527 
1528 if(std::abs(dx2) < std::abs(f1.x)*rb.dx_tolerance || std::abs(dx2)
1529 < rb.abs_tol_min) {
1530 std::ostringstream outstr;
1531 outstr << "Step size underflow in adaptive_sampling at depth="
1532 << back.depth << ", x= " << f1.x;
1533 throw c2_exception(outstr.str().c_str());
1534 }
1535 
1536 fill_fblock(f1);
1537 
1538 if(c2_isnan(f1.y) || f1.ypbad || f1.yppbad) {
1539 // can't go any further if a nan has appeared
1540 bad_x_point=f1.x;
1541 throw c2_exception("NaN encountered while sampling function");
1542 }
1543 
1544 float_type eps;
1545 if(rb.derivs==2) {
1546 // this is code from connector_function to compute the value at the midpoint
1547 // it is re-included here to avoid constructing a complete c2connector
1548 // just to find out if we are close enough
1549 float_type ff0=(8*(f0.y + f2.y) + 5*(f0.yp - f2.yp)*dx2
1550 + (f0.ypp+f2.ypp)*dx2*dx2)*0.0625;
1551 // we are converging as at least x**5 and bisecting,
1552 // so real error on final step is smaller
1553 eps=std::abs(ff0-f1.y)/32.0;
1554 } else {
1555 // there are two tolerances to meet... the shift in the estimate
1556 // of the actual point,
1557 // and the difference between the current points and the extremum
1558 // build all the coefficients needed to construct the local parabola
1559 float_type ypcenter, ypp;
1560 if (rb.derivs==1) {
1561 // linear extrapolation error using exact derivs
1562 eps = (std::abs(f0.y+f0.yp*dx2-f1.y)+std::abs(f2.y-f2.yp*dx2-f1.y))*0.125;
1563 ypcenter=2*f1.yp*dx2; // first deriv scaled so this interval is on [-1,1]
1564 ypp=2*(f2.yp-f0.yp)*dx2*dx2;
1565 } else {
1566 // linear interpolation error without derivs if we are at top level
1567 // or 3-point parabolic interpolation estimates from previous level
1568 ypcenter=(f2.y-f0.y)*0.5; // derivative estimate at center
1569 ypp=(f2.y+f0.y-2*f1.y); // second deriv estimate
1570 if(back.depth==0) eps=std::abs((f0.y+f2.y)*0.5 - f1.y)*2;
1571 // penalize first step
1572 else eps=std::abs(f1.y-back.previous_estimate)*0.25;
1573 }
1574 float_type ypleft=ypcenter-ypp; // derivative at left edge
1575 float_type ypright=ypcenter+ypp; // derivative at right edge
1576 float_type extremum_eps=0;
1577 if((ypleft*ypright) <=0) // y' changes sign if we have an extremum
1578 {
1579 // compute position and value of the extremum this way
1580 float_type xext=-ypcenter/ypp;
1581 float_type yext=f1.y + xext*ypcenter + 0.5*xext*xext*ypp;
1582 // and then find the the smallest offset of it from a point,
1583 // looking in the left or right side
1584 if(xext <=0) extremum_eps=std::min(std::abs(f0.y-yext), std::abs(f1.y-yext));
1585 else extremum_eps=std::min(std::abs(f2.y-yext), std::abs(f1.y-yext));
1586 }
1587 eps=std::max(eps, extremum_eps); // if previous shot was really bad, keep trying
1588 }
1589 
1590 if(eps < rb.abs_tol || eps < std::abs(f1.y)*rb.rel_tol) {
1591 if(rb.out) {
1592 // we've met the tolerance, and are building a function, append two connectors
1593 rb.out->append_function(
1594 *new c2_connector_function_p<float_type>(f0, f1, true, 0.0)
1595 );
1596 rb.out->append_function(
1597 *new c2_connector_function_p<float_type>(f1, f2, true, 0.0)
1598 );
1599 }
1600 if(rb.xvals && rb.yvals) {
1601 rb.xvals->push_back(f0.x);
1602 rb.xvals->push_back(f1.x);
1603 rb.yvals->push_back(f0.y);
1604 rb.yvals->push_back(f1.y);
1605 // the value at f2 will get pushed in the next segment... it is not forgotten
1606 }
1607 } else {
1608 top.depth=back.depth+1; // increment depth counter
1609 
1610 // save the last things we need from back before a push happens, in case
1611 // the push causes a reallocation and moves the whole stack.
1612 size_t f0index=back.f0index, f2index=back.f2index;
1613 float_type left=0, right=0;
1614 if(rb.derivs==0) {
1615 // compute three-point parabolic interpolation estimate of right-hand
1616 // and left-hand midpoint
1617 left=(6*f1.y + 3*f0.y - f2.y) * 0.125;
1618 right=(6*f1.y + 3*f2.y - f0.y) * 0.125;
1619 }
1620 
1621 top.f0index=f1index; top.f2index=f2index;
1622 // insert pointers to right side data into our recursion block
1623 top.previous_estimate=right;
1624 rb_stack.push_back(top);
1625 
1626 top.f0index=f0index; top.f2index=f1index;
1627 // insert pointers to left side data into our recursion block
1628 top.previous_estimate=left;
1629 rb_stack.push_back(top);
1630 }
1631 }
1632 }
1633 
1634 template <typename float_type> c2_piecewise_function_p<float_type> *
1635 c2_function<float_type>::adaptively_sample(
1636 float_type amin, float_type amax,
1637 float_type abs_tol, float_type rel_tol,
1638 int derivs, std::vector<float_type> *xvals,
1639 std::vector<float_type> *yvals) const throw(c2_exception)
1640 {
1641 c2_fblock<float_type> f0, f2;
1642 c2_sample_recur rb;
1643 std::vector< recur_item > rb_stack;
1644 rb_stack.reserve(20); // enough for most operations
1645 rb.rb_stack=&rb_stack;
1646 rb.out=0;
1647 if(derivs==2) rb.out=new c2_piecewise_function_p<float_type>();
1648 c2_ptr<float_type> pieces(*rb.out);
1649 rb.rel_tol=rel_tol;
1650 rb.abs_tol=abs_tol;
1651 rb.xvals=xvals;
1652 rb.yvals=yvals;
1653 rb.derivs=derivs;
1654 rb.inited=false;
1655 
1656 if(xvals && yvals) {
1657 xvals->clear();
1658 yvals->clear();
1659 }
1660 
1661 // create xgrid as a automatic-variable copy of the sampling
1662 // grid so the exception handler correctly
1663 // disposes of it.
1664 std::vector<float_type> xgrid;
1665 get_sampling_grid(amin, amax, xgrid);
1666 int np=xgrid.size();
1667 
1668 f2.x=xgrid[0];
1669 fill_fblock(f2);
1670 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1671 // can't go any further if a nan has appeared
1672 bad_x_point=f2.x;
1673 throw c2_exception("NaN encountered while sampling function");
1674 }
1675 
1676 for(int i=0; i<np-1; i++) {
1677 f0=f2; // copy upper bound to lower before computing new upper bound
1678 
1679 f2.x=xgrid[i+1];
1680 fill_fblock(f2);
1681 if(c2_isnan(f2.y) || f2.ypbad || f2.yppbad) {
1682 // can't go any further if a nan has appeared
1683 bad_x_point=f2.x;
1684 throw c2_exception("NaN encountered while sampling function");
1685 }
1686 
1687 rb.f0=&f0; rb.f1=&f2;
1688 sample_step(rb);
1689 }
1690 if(xvals && yvals) { // push final point in vector
1691 xvals->push_back(f2.x);
1692 yvals->push_back(f2.y);
1693 }
1694 
1695 if(rb.out) rb.out->set_sampling_grid(xgrid);
1696 // reflect old sampling grid, which still should be right
1697 pieces.release_for_return();
1698 // unmanage the piecewise_function so we can return it
1699 return rb.out;
1700 }