// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // // /** * \file * \brief Provides the headers for the general c2_function algebra which supports * fast, flexible operations on piecewise-twice-differentiable functions * * \author Created by R. A. Weller and Marcus H. Mendenhall on 7/9/05. * \author Copyright 2005 __Vanderbilt University__. All rights reserved. * * \version c2_function.hh,v 1.238 2008/05/22 12:45:19 marcus Exp * \see \ref c2_factory "Factory Functions" for information on constructing things in here */ #ifndef __has_c2_function_hh #define __has_c2_function_hh 1 // MSVC does not automatically define numerical constants such as M_PI without this. // this came from the msdn website, so it should be right... #ifdef _MSC_VER #define _USE_MATH_DEFINES #define c2_isnan _isnan #define c2_isfinite _finite #else #define c2_isnan std::isnan #define c2_isfinite std::isfinite #endif #include #include #include #include #include #include #include /// \brief the exception class for c2_function operations. class c2_exception : public std::exception { public: /// \brief construct the exception with an error message /// \param msgcode the message c2_exception(const char msgcode[]) : info(msgcode) { } virtual ~c2_exception() throw() { } /** Returns a C-style character string describing the general cause * of the current error. */ virtual const char* what() const throw() { return info.c_str(); } private: std::string info; }; // put these forward references here, and with a bogus typename to make swig happy. template class c2_composed_function_p; template class c2_sum_p; template class c2_diff_p; template class c2_product_p; template class c2_ratio_p; template class c2_piecewise_function_p; template class c2_quadratic_p; template class c2_ptr; /** \defgroup abstract_classes Abstract Classes \defgroup arithmetic_functions Arithmetic Functions \defgroup math_functions Mathemetical Functions \defgroup parametric_functions Parametric Families of Functions \defgroup interpolators Interpolating Functions \defgroup containers Functions which are containers for, or functions of, other functions \defgroup factories Factory classes which reduce silly template typing \defgroup transforms Classes which provide coordinate system transformations, wih derivatives */ /// \brief structure used to hold evaluated function data at a point. /// /// Contains all the information for the function at one point. template class c2_fblock { public: /// \brief the abscissa float_type x; /// \brief the value of the function at \a x float_type y; /// \brief the derivative at \a x float_type yp; /// \brief the second derivative at \a x float_type ypp; /// flag, filled in by c2_function::fill_fblock(), indicating the derivative is NaN of Inf bool ypbad; /// flag, filled in by c2_function::fill_fblock(), indicating the second derivative is NaN of Inf bool yppbad; }; /** \brief the parent class for all c2_functions. \ingroup abstract_classes c2_functions know their value, first, and second derivative at almost every point. They can be efficiently combined with binary operators, via c2_binary_function, composed via c2_composed_function_, have their roots found via find_root(), and be adaptively integrated via partial_integrals() or integral(). They also can carry information with them about how to find 'interesting' points on the function. This information is set with set_sampling_grid() and extracted with get_sampling_grid(). Particularly important subclasses are the interpolating functions classes, interpolating_function , lin_log_interpolating_function, log_lin_interpolating_function, log_log_interpolating_function, and arrhenius_interpolating_function, as well as the template functions inverse_integrated_density_function(). For a discussion of memory management, see \ref memory_management */ template class c2_function { public: /// \brief get versioning information for the header file /// \return the CVS Id string const std::string cvs_header_vers() const { return "c2_function.hh,v 1.238 2008/05/22 12:45:19 marcus Exp"; } /// \brief get versioning information for the source file /// \return the CVS Id string const std::string cvs_file_vers() const ; public: /// \brief destructor virtual ~c2_function() { if(sampling_grid && !no_overwrite_grid) delete sampling_grid; if(root_info) delete root_info; if(owner_count) { std::ostringstream outstr; outstr << "attempt to delete an object with non-zero ownership in class "; outstr << typeid(*this).name() << std::endl; throw c2_exception(outstr.str().c_str()); } } /// \brief get the value and derivatives. /// /// There is required checking for null pointers on the derivatives, /// and most implementations should operate faster if derivatives are not needed. /// \param[in] x the point at which to evaluate the function /// \param[out] yprime the first derivative (if pointer is non-null) /// \param[out] yprime2 the second derivative (if pointer is non-null) /// \return the value of the function virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) =0 ; // { return 0; }; /// \brief evaluate the function in the classic way, ignoring derivatives. /// \param x the point at which to evaluate /// \return the value of the function inline float_type operator () (float_type x) const throw(c2_exception) { return value_with_derivatives(x, (float_type *)0, (float_type *)0); } /// \brief get the value and derivatives. /// /// \param[in] x the point at which to evaluate the function /// \param[out] yprime the first derivative (if pointer is non-null) /// \param[out] yprime2 the second derivative (if pointer is non-null) /// \return the value of the function inline float_type operator () (float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { return value_with_derivatives(x, yprime, yprime2); } /// \brief solve f(x)==value very efficiently, with explicit knowledge of derivatives of the function /// /// find_root solves by iterated inverse quadratic extrapolation for a solution to f(x)=y. It /// includes checks against bad convergence, so it should never be able to fail. Unlike typical /// secant method or fancier Brent's method finders, this does not depend in any strong wasy on the /// brackets, unless the finder has to resort to successive approximations to close in on a root. /// Often, it is possible to make the brackets equal to the domain of the function, if there is /// any clue as to where the root lies, as given by the parameter \a start. /// \param lower_bracket the lower bound for the search /// \param upper_bracket the upper bound for the search. Function sign must be /// opposite to that at \a lower_bracket /// \param start starting value for the search /// \param value the value of the function being sought (solves f(x) = \a value) /// \param[out] error If pointer is zero, errors raise exception. Otherwise, returns error here. /// \param[out] final_yprime If pointer is not zero, return derivative of function at root /// \param[out] final_yprime2 If pointer is not zero, return second derivative of function at root /// \return the position of the root. /// \see \ref rootfinder_subsec "Root finding sample" float_type find_root(float_type lower_bracket, float_type upper_bracket, float_type start, float_type value, int *error=0, float_type *final_yprime=0, float_type *final_yprime2=0 ) const throw(c2_exception) ; // solve f(x)=value /// \brief for points in xgrid, adaptively return Integral[f(x),{x,xgrid[i],xgrid[i+1]}] and return in vector, along with sum /// /// partial_integrals uses a method with an error O(dx**10) with full information from the derivatives, /// and falls back to lower order methods if informed of incomplete derivatives. /// It uses exact midpoint splitting of the intervals for recursion, resulting in no recomputation of the function /// during recursive descent at previously computed points. /// \param xgrid points between which to evaluate definite integrals. /// \param partials if non-NULL, a vector in which to receive the partial integrals. /// It will automatically be sized apprpropriately, if provided, to contain \a n - 1 elements where \a n is the length of \a xgrid /// \param abs_tol the absolute error bound for each segment /// \param rel_tol the fractional error bound for each segment. /// If the error is smaller than either the relative or absolute tolerance, the integration step is finished. /// \param derivs number of derivatives to trust, which sets the order of the integrator. The order /// is 3*\a derivs + 4. \a derivs can be 0, 1, or 2. /// \param adapt if true, use recursive adaptation, otherwise do simple evaluation on the grid provided /// with no error checking. /// \param extrapolate if true, use simple Richardson extrapolation on the final 2 steps to reduce the error. /// \return sum of partial integrals, which is the definite integral from the first value in \a xgrid to the last. float_type partial_integrals(std::vector xgrid, std::vector *partials = 0, float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) const throw(c2_exception); /// \brief a fully-automated integrator which uses the information provided by the get_sampling_grid() function /// to figure out what to do. /// /// It returns the integral of the function over the domain requested /// with error tolerances as specified. It is just a front-end to partial_integrals() /// /// \param xmin lower bound of the domain for integration /// \param xmax upper bound of the domain for integration /// \param partials if non-NULL, a vector in which to receive the partial integrals. /// It will automatically be sized appropriately, if provided, to contain \a n - 1 elements where \a n is the length of \a xgrid /// \param abs_tol the absolute error bound for each segment /// \param rel_tol the fractional error bound for each segment. /// If the error is smaller than either the relative or absolute tolerance, the integration step is finished. /// \param derivs number of derivatives to trust, which sets the order of the integrator. The order /// is 3*\a derivs + 4. \a derivs can be 0, 1, or 2. /// \param adapt if true, use recursive adaptation, otherwise do simple evaluation on the grid provided /// with no error checking. /// \param extrapolate if true, use simple Richardson extrapolation on the final 2 steps to reduce the error. /// \return sum of partial integrals, which is the definite integral from the first value in \a xgrid to the last. float_type integral(float_type xmin, float_type xmax, std::vector *partials = 0, float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, bool adapt=true, bool extrapolate=true) const throw(c2_exception); /// \brief create a c2_piecewise_function_p from c2_connector_function_p segments which /// is a representation of the parent function to the specified accuracy, but maybe much cheaper to evaluate /// /// This method has three modes, depending on the \a derivs flag. /// /// If \a derivs is 2, /// it computes a c2_piecewise_function_p representation of its parent function, which may be a much faster /// function to use in codes if the parent function is expensive. If \a xvals and \a yvals are non-null, /// it will also fill them in with the function values at each grid point the adaptive algorithm chooses. /// /// If \a derivs is 1, this does not create the connectors, /// and returns an null pointer, but will fill in the \a xvals and \a yvals /// vectors with values of the function at points such that the linear interpolation error between the points /// is bounded by the tolerance values given. Because it uses derivative information from the function to manage the /// error control, it is almost completely free of issues with missing periods of oscillatory functions, /// even with no information provided in the sampling grid. /// This is typically useful for sampling a function for plotting. /// /// If \a derivs is 0, this does something very like what it does if \a derivs = 1, but without derivatives. /// Instead, to compute the intermediate value of the function for error control, it just uses /// 3-point parabolic interpolation. This is useful amost exclusively for converting a non-c2_function, /// with no derivatives, but wrapped in a c2_classic_function wrapper, into a table of values to seed an interpolating_function_p. /// Note, however, that without derivatives, this is very susceptible to missing periods of oscillatory /// functions, so it is important to set a sampling grid which isn't too much coarser than the typical oscillations. /// /// \note the \a sampling_grid of the returned function matches the \a sampling_grid of its parent. /// \see \ref sample_function_for_plotting "Adaptive Sampling Examples" /// \param xmin lower bound of the domain for sampling /// \param xmax upper bound of the domain for sampling /// \param abs_tol the absolute error bound for each segment /// \param rel_tol the fractional error bound for each segment. /// \param derivs if 0 or 1, return a useless function, but fill in the \a xvals and \a yvals vectors (if non-null). /// Also, if 0 or 1, tolerances refer to linear interpolation, not high-order interpolation. /// If 2, return a full piecewise collection of c2_connector_function_p segments. See discussion above. /// \param [in,out] xvals vector of abscissas at which the function was actually sampled (if non-null) /// \param [in,out] yvals vector of function values corresponding to \a xvals (if non-null) /// \return a new, sampled representation, if \a derivs is 2. A null pointer if \a derivs is 0 or 1. c2_piecewise_function_p *adaptively_sample(float_type xmin, float_type xmax, float_type abs_tol=1e-12, float_type rel_tol=1e-12, int derivs=2, std::vector *xvals=0, std::vector *yvals=0) const throw(c2_exception); /// \brief return the lower bound of the domain for this function as set by set_domain() inline float_type xmin() const { return fXMin; } /// \brief return the upper bound of the domain for this function as set by set_domain() inline float_type xmax() const { return fXMax; } /// \brief set the domain for this function. void set_domain(float_type xmin, float_type xmax) { fXMin=xmin; fXMax=xmax; } /// \brief this is a counter owned by the function but which can be used to monitor efficiency of algorithms. /// /// It is not maintained automatically in general! The root finder, integrator, and sampler do increment it. /// \return number of evaluations logged since last reset. volatile size_t get_evaluations() const { return evaluations; } /// \brief reset the counter void reset_evaluations() const { evaluations=0; } // evaluations are 'invisible' to constant /// \brief count evaluations inline void increment_evaluations() const { evaluations++; } /// \brief check that a vector is monotonic, throw an exception if not, and return a flag if it is reversed /// /// \param data a vector of data points which are expected to be monotonic. /// \param message an informative string to include in an exception if this throws c2_exception /// \return true if in decreasing order, false if increasing bool check_monotonicity(const std::vector &data, const char message[]) const throw(c2_exception); /// \brief establish a grid of 'interesting' points on the function. /// /// The sampling grid describes a reasonable initial set of points to look at the function. /// this should generally be set at a scale which is quite coarse, and sufficient for initializing /// adaptive integration or possibly root bracketing. For sampling a function to build a new interpolating /// function, one may want to refine this for accuracy. However, interpolating_functions themselves /// return their original X grid by default, so refining the grid in this case might be a bad idea. /// \param grid a vector of abscissas. The contents is copied into an internal vector, so the \a grid can be discarded after passingin. virtual void set_sampling_grid(const std::vector &grid) throw(c2_exception); /// \brief get the sampling grid, which may be a null pointer /// \return pointer to the sampling grid std::vector *get_sampling_grid_pointer() const { return sampling_grid; } /// \brief return the grid of 'interesting' points along this function which lie in the region requested /// /// if a sampling grid is defined, work from there, otherwise return vector of (xmin, xmax) /// \param xmin the lower bound for which the function is to be sampled /// \param xmax the upper bound for which the function is to be sampled /// \param [in,out] grid filled vector containing the samplng grid. virtual void get_sampling_grid(float_type xmin, float_type xmax, std::vector &grid) const ; /// \brief clean up endpoints on a grid of points /// \param[in,out] result the sampling grid with excessively closely space endpoints removed. /// The grid is modified in place. void preen_sampling_grid(std::vector *result) const; /// \brief refine a grid by splitting each interval into more intervals /// \param [in,out] grid the grid to refine in place /// \param refinement the number of new steps for each old step void refine_sampling_grid(std::vector &grid, size_t refinement) const; /// \brief create a new c2_function from this one which is normalized on the interval /// \param xmin lower bound of the domain for integration /// \param xmax upper bound of the domain for integration /// \param norm the desired integral for the function over the region /// \return a new c2_function with the desired \a norm. c2_function &normalized_function(float_type xmin, float_type xmax, float_type norm=1.0) const throw(c2_exception); /// \brief create a new c2_function from this one which is square-normalized on the interval /// \param xmin lower bound of the domain for integration /// \param xmax upper bound of the domain for integration /// \param norm the desired integral for the function over the region /// \return a new c2_function with the desired \a norm. c2_function &square_normalized_function(float_type xmin, float_type xmax, float_type norm=1.0) const throw(c2_exception); /// \brief create a new c2_function from this one which is square-normalized with the provided \a weight on the interval /// \param xmin lower bound of the domain for integration /// \param xmax upper bound of the domain for integration /// \param weight a c2_function providing the weight /// \param norm the desired integral for the function over the region /// \return a new c2_function with the desired \a norm. c2_function &square_normalized_function( float_type xmin, float_type xmax, const c2_function &weight, float_type norm=1.0) const throw(c2_exception); /// \brief factory function to create a c2_sum_p from a regular algebraic expression. /// \param rhs the right-hand term of the sum /// \return a new c2_function c2_sum_p &operator + (const c2_function &rhs) const { return *new c2_sum_p(*this, rhs); } /// \brief factory function to create a c2_diff_p from a regular algebraic expression. /// \param rhs the right-hand term of the difference /// \return a new c2_function c2_diff_p &operator - (const c2_function &rhs) const { return *new c2_diff_p(*this, rhs); } /// \brief factory function to create a c2_product_p from a regular algebraic expression. /// \param rhs the right-hand term of the product /// \return a new c2_function c2_product_p &operator * (const c2_function &rhs) const { return *new c2_product_p(*this, rhs); } /// \brief factory function to create a c2_ratio_p from a regular algebraic expression. /// \param rhs the right-hand term of the ratio (the denominator) /// \return a new c2_function c2_ratio_p &operator / (const c2_function &rhs) const { return *new c2_ratio_p(*this, rhs); } /// \brief compose this function outside another. /// \param inner the inner function /// \return the composed function /// \anchor compose_operator c2_composed_function_p & operator ()(const c2_function &inner) const { return *new c2_composed_function_p((*this), inner); } /// \brief Find out where a calculation ran into trouble, if it got a nan. /// If the most recent computation did not return a nan, this is undefined. /// \return \a x value of point at which something went wrong, if integrator (or otherwise) returned a nan. float_type get_trouble_point() const { return bad_x_point; } /// \brief increment our reference count. Destruction is only legal if the count is zero. void claim_ownership() const { owner_count++; } /// \brief decrement our reference count. Do not destroy at zero. /// \return final owner count, to check whether object should disappear. size_t release_ownership_for_return() const throw(c2_exception) { if(!owner_count) { std::ostringstream outstr; outstr << "attempt to release ownership of an unowned function in class "; outstr << typeid(*this).name() << std::endl; throw c2_exception(outstr.str().c_str()); } owner_count--; return owner_count; } /// \brief decrement our reference count. If the count reaches zero, destroy ourself. void release_ownership() const throw(c2_exception) { if(!release_ownership_for_return()) delete this; } /// \brief get the reference count, mostly for debugging /// \return the count size_t count_owners() const { return owner_count; } protected: c2_function(const c2_function &src) : sampling_grid(0), no_overwrite_grid(false), fXMin(src.fXMin), fXMax(src.fXMax), root_info(0), owner_count(0) {} // copy constructor only copies domain, and is only for internal use c2_function() : sampling_grid(0), no_overwrite_grid(0), fXMin(-std::numeric_limits::max()), fXMax(std::numeric_limits::max()), root_info(0), owner_count(0) {} // prevent accidental naked construction (impossible any since this has pure virtual methods) // this should only be called very early on, by a constructor, before anyone else // sets a sampling grid, or it will leak memory virtual void set_sampling_grid_pointer(std::vector &grid) { if (sampling_grid && !no_overwrite_grid) delete sampling_grid; // grid was ours, lose it. sampling_grid=&grid; no_overwrite_grid=1; } std::vector * sampling_grid; bool no_overwrite_grid; float_type fXMin, fXMax; mutable size_t evaluations; /// \brief this point may be used to record where a calculation ran into trouble mutable float_type bad_x_point; public: /// \brief fill in a c2_fblock... a shortcut for the integrator & sampler /// \param [in,out] fb the block to fill in with information inline void fill_fblock(c2_fblock &fb) const throw(c2_exception) { fb.y=value_with_derivatives(fb.x, &fb.yp, &fb.ypp); fb.ypbad=c2_isnan(fb.yp) || !c2_isfinite(fb.yp); fb.yppbad=c2_isnan(fb.ypp) || !c2_isfinite(fb.ypp); increment_evaluations(); } private: /// \brief the data element for the internal recursion stack for the sampler and integrator struct recur_item { c2_fblock f1; size_t depth; float_type previous_estimate, abs_tol, step_sum; bool done; size_t f0index, f2index; }; /// \brief structure used to pass information recursively in integrator. /// /// the \a abs_tol is scaled by a factor of two at each division. /// Everything else is just passed down. struct c2_integrate_recur { c2_fblock *f0, *f1; float_type abs_tol, rel_tol, eps_scale, extrap_coef, extrap2, dx_tolerance, abs_tol_min; std::vector< recur_item > *rb_stack; int derivs; bool adapt, extrapolate, inited; }; /// \brief structure used to pass information recursively in sampler. /// struct c2_sample_recur { c2_fblock *f0, *f1; float_type abs_tol, rel_tol, dx_tolerance, abs_tol_min; int derivs; c2_piecewise_function_p *out; std::vector *xvals, *yvals; std::vector< recur_item > *rb_stack; bool inited; }; /// \brief structure used to hold root bracketing information /// struct c2_root_info { c2_fblock lower, upper; bool inited; }; /// \brief Carry out the recursive subdivision and integration. /// /// This passes information recursively through the \a recur block pointer /// to allow very efficient recursion. /// \param rb a pointer to the recur struct. float_type integrate_step(struct c2_integrate_recur &rb) const throw(c2_exception); /// \brief Carry out the recursive subdivision for sampling. /// /// This passes information recursively through the \a recur block pointer /// to allow very efficient recursion. /// \param rb a pointer to the recur struct. void sample_step(struct c2_sample_recur &rb) const throw(c2_exception); /// this carry a memory of the last root bracketing, /// to avoid the necessity of evaluating the function on the brackets every time /// if the brackets have not been changed. /// it is declared as a pointer, since many c2_functions may never need one allocated mutable struct c2_root_info *root_info; mutable size_t owner_count; }; /// \brief a container into which any conventional c-style function can be dropped, /// to create a degenerate c2_function without derivatives. /// Mostly useful for sampling into interpolating functions. /// construct a reference to this with c2_classic_function() /// \ingroup containers /// The factory function c2_factory::classic_function() creates *new c2_classic_function_p() template class c2_classic_function_p : public c2_function { public: /// \brief construct the container /// \param c_func a pointer to a conventional c-style function c2_classic_function_p(const float_type (*c_func)(float_type)) : c2_function(), func(c_func) {} /// \copydoc c2_function::value_with_derivatives /// Uses the internal function pointer set by set_function(). virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { if(!func) throw c2_exception("c2_classic_function called with null function"); if(yprime) *yprime=0; if(yprime2) *yprime2=0; return func(x); } ~c2_classic_function_p() { } protected: /// \brief pointer to our function const float_type (*func)(float_type); }; /// \brief create a container for a c2_function which handles the reference counting. /// \ingroup containers /// It is useful as a smart container to hold a c2_function and keep the reference count correct. /// The recommended way for a class to store a c2_function which is handed in from the outside /// is for it to have a c2_ptr member into which the passed-in function is stored. /// This way, when the class instance is deleted, it will automatically dereference any function /// which it was handed. /// /// This class contains a copy constructor and operator=, to make it fairly easy to make /// a std::vector of these objects, and have it work as expected. template class c2_const_ptr { public: /// \brief construct the container with no function c2_const_ptr() : func(0) {} /// \brief construct the container with a pre-defined function /// \param f the function to store c2_const_ptr(const c2_function &f) : func(0) { set_function(&f); } /// \brief copy constructor /// \param src the container to copy c2_const_ptr(const c2_const_ptr &src) : func(0) { set_function(src.get_ptr()); } /// \brief fill the container with a new function, or clear it with a null pointer /// \param f the function to store, releasing any previously held function void set_function(const c2_function *f) { if(func) func->release_ownership(); func=f; if(func) func->claim_ownership(); } /// \brief fill the container from another container /// \param f the container to copy void operator =(const c2_const_ptr &f) { set_function(f.get_ptr()); } /// \brief fill the container with a function /// \param f the function void operator =(const c2_function &f) { set_function(&f); } /// \brief release the function without destroying it, so it can be returned from a function /// /// This is usually the very last line of a function before the return statement, so that /// any exceptions that happen during execution of the function will cause proper cleanup. /// Once the function has been released from its container this way, it is an orhpaned object /// until the caller claims it, so it could get lost if an exception happens. void release_for_return() throw(c2_exception) { if(func) func->release_ownership_for_return(); func=0; } /// \brief clear the function /// /// Any attempt to use this c2_plugin_function_p throws an exception if the saved function is cleared. void unset_function(void) { set_function(0); } /// \brief destructor ~c2_const_ptr() { set_function(0); } /// \brief get a reference to our owned function inline const c2_function &get() const throw(c2_exception) { if(!func) throw c2_exception("c2_ptr accessed uninitialized"); return *func; } /// \brief get an unchecked pointer to our owned function inline const c2_function *get_ptr() const { return func; } /// \brief get a checked pointer to our owned function inline const c2_function *operator -> () const { return &get(); } /// \brief check if we have a valid function bool valid() const { return func != 0; } /// \brief type coercion operator which lets us use a pointer as if it were a const c2_function operator const c2_function& () const { return this->get(); } /// \brief convenience operator to make us look like a function /// \param x the value at which to evaluate the contained function /// \return the evaluated function /// \note If you using this repeatedly, do const c2_function &func=ptr; /// and use func(x). Calling this operator wastes some time, since it checks the validity of the /// pointer every time. float_type operator()(float_type x) const throw(c2_exception) { return get()(x); } /// \brief convenience operator to make us look like a function /// \param x the value at which to evaluate the contained function /// \param yprime the derivative /// \param yprime2 the second derivative /// \return the evaluated function /// \note If you using this repeatedly, do const c2_function &func=ptr; /// and use func(x). Calling this operator wastes some time, since it checks the validity of the /// pointer every time. float_type operator()(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { return get().value_with_derivatives(x, yprime, yprime2); } /// \brief factory function to create a c2_sum_p from a regular algebraic expression. /// \param rhs the right-hand term of the sum /// \return a new c2_function c2_sum_p &operator + (const c2_function &rhs) const throw(c2_exception) { return *new c2_sum_p(get(), rhs); } /// \brief factory function to create a c2_diff_p from a regular algebraic expression. /// \param rhs the right-hand term of the difference /// \return a new c2_function c2_diff_p &operator - (const c2_function &rhs) const throw(c2_exception) { return *new c2_diff_p(get(), rhs); } /// \brief factory function to create a c2_product_p from a regular algebraic expression. /// \param rhs the right-hand term of the product /// \return a new c2_function c2_product_p &operator * (const c2_function &rhs) const throw(c2_exception) { return *new c2_product_p(get(), rhs); } /// \brief factory function to create a c2_ratio_p from a regular algebraic expression. /// \param rhs the right-hand term of the ratio (the denominator) /// \return a new c2_function c2_ratio_p &operator / (const c2_function &rhs) const throw(c2_exception) { return *new c2_ratio_p(get(), rhs); } /// \brief compose this function outside another. /// \param inner the inner function /// \return the composed function c2_composed_function_p & operator ()(const c2_function &inner) const throw(c2_exception) { return *new c2_composed_function_p(get(), inner); } protected: const c2_function * func; }; /// \brief create a container for a c2_function which handles the reference counting. /// \ingroup containers /// /// \see c2_const_ptr and \ref memory_management "Use of c2_ptr for memory management" template class c2_ptr : public c2_const_ptr { public: /// \brief construct the container with no function c2_ptr() : c2_const_ptr() {} /// \brief construct the container with a pre-defined function /// \param f the function to store c2_ptr(c2_function &f) : c2_const_ptr() { set_function(&f); } /// \brief copy constructor /// \param src the container to copy c2_ptr(const c2_ptr &src) : c2_const_ptr() { set_function(src.get_ptr()); } /// \brief get a checked pointer to our owned function inline c2_function &get() const throw(c2_exception) { return *const_cast*>(&c2_const_ptr::get()); } /// \brief get an unchecked pointer to our owned function inline c2_function *get_ptr() const { return const_cast*>(this->func); } /// \brief get a checked pointer to our owned function inline c2_function *operator -> () const { return &get(); } /// \brief fill the container from another container /// \param f the container to copy void operator =(const c2_ptr &f) { set_function(f.get_ptr()); } /// \brief fill the container with a function /// \param f the function void operator =(c2_function &f) { set_function(&f); } private: /// \brief hidden non-const-safe version of operator= void operator =(const c2_const_ptr &f) { } /// \brief hidden non-const-safe version of operator= void operator =(const c2_function &f) { } }; /// \brief create a non-generic container for a c2_function which handles the reference counting. /// \ingroup containers /// /// \see c2_const_ptr and \ref memory_management "Use of c2_ptr for memory management" /// /// \note Overuse of this class will generate massive bloat. Use c2_ptr and c2_const_ptr if you don't _really_ need specific pointer types. /// \see \ref memory_management "Use of c2_ptr for memory management" /* template class c2_class > class c2_typed_ptr : public c2_const_ptr { public: /// \brief construct the container with no function c2_typed_ptr() : c2_ptr() {} /// \brief construct the container with a pre-defined function /// \param f the function to store c2_typed_ptr(c2_class &f) : c2_const_ptr() { this->set_function(&f); } /// \brief copy constructor /// \param src the container to copy c2_typed_ptr(const c2_typed_ptr &src) : c2_const_ptr() { this->set_function(src.get_ptr()); } /// \brief get a reference to our owned function inline c2_class &get() const throw(c2_exception) { return *static_cast *>(const_cast*>(&c2_const_ptr::get())); } /// \brief get a checked pointer to our owned function inline c2_class *operator -> () const { return &get(); } /// \brief get an unchecked pointer to our owned function inline c2_class *get_ptr() const { return static_cast *>(const_cast*>(this->func)); } /// \brief type coercion operator which lets us use a pointer as if it were a c2_function operator c2_class&() const { return get(); } /// \brief fill the container from another container /// \param f the container to copy void operator =(const c2_typed_ptr &f) { set_function(f.get_ptr()); } /// \brief fill the container with a function /// \param f the function void operator =(c2_class &f) { set_function(&f); } private: /// \brief hidden downcasting version of operator= void operator =(const c2_const_ptr &f) { } /// \brief hidden downcasting version of operator=. Use an explicit dynamic_cast&>(f) if you need to try this. void operator =(const c2_function &f) { } }; */ /// \brief a container into which any other c2_function can be dropped, to allow expressions /// with replacable components. /// \ingroup containers ///It is useful for plugging different InterpolatingFunctions into a c2_function expression. ///It saves a lot of effort in other places with casting away const declarations. /// /// It is also useful as a wrapper for a function if it is necessary to have a copy of a function /// which has a different domain or sampling grid than the parent function. This can be /// be used, for example, to patch badly-behaved functions with c2_piecewise_function_p by /// taking the parent function, creating two plugins of it with domains on each side of the /// nasty bit, and then inserting a nice function in the hole. /// /// This can also be used as a fancier c2_ptr which allows direct evaluation /// instead of having to dereference the container first. /// /// The factory function c2_factory::plugin_function() creates *new c2_plugin_function_p() template class c2_plugin_function_p : public c2_function { public: /// \brief construct the container with no function c2_plugin_function_p() : c2_function(), func() {} /// \brief construct the container with a pre-defined function c2_plugin_function_p(c2_function &f) : c2_function(),func(f) { } /// \brief fill the container with a new function, or clear it with a null pointer /// and copy our domain void set_function(c2_function *f) { func.set_function(f); if(f) set_domain(f->xmin(), f->xmax()); } /// \copydoc c2_function::value_with_derivatives /// Uses the internal function pointer set by set_function(). virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { if(!func.valid()) throw c2_exception("c2_plugin_function_p called uninitialized"); return func->value_with_derivatives(x, yprime, yprime2); } /// \brief destructor ~c2_plugin_function_p() { } /// \brief clear our function void unset_function() { func.unset_function(); } virtual void get_sampling_grid(float_type xmin, float_type xmax, std::vector &grid) const { if(!func.valid()) throw c2_exception("c2_plugin_function_p called uninitialized"); if(this->sampling_grid) c2_function::get_sampling_grid(xmin, xmax, grid); else func->get_sampling_grid(xmin, xmax, grid); } protected: c2_ptr func; }; /// \brief a c2_plugin_function_p which promises not to fiddle with the plugged function. /// \ingroup containers /// /// The factory function c2_factory::const_plugin_function() creates *new c2_const_plugin_function_p() template class c2_const_plugin_function_p : public c2_plugin_function_p { public: /// \brief construct the container with no function c2_const_plugin_function_p() : c2_plugin_function_p() {} /// \brief construct the container with a pre-defined function c2_const_plugin_function_p(const c2_function &f) : c2_plugin_function_p() { set_function(&f); } /// \brief fill the container with a new function, or clear it with a null pointer void set_function(const c2_function *f) { c2_plugin_function_p::set_function(const_cast*>(f)); } /// \brief destructor ~c2_const_plugin_function_p() { } /// \brief get a const reference to our owned function, for direct access const c2_function &get() const throw(c2_exception) { return this->func.get(); } }; /// \brief Provides support for c2_function objects which are constructed from two other c2_function /// objects. /// \ingroup abstract_classes template class c2_binary_function : public c2_function { public: /// \brief function to manage the binary operation, used by c2_binary_function::value_with_derivatives() /// virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception) { if(stub) throw c2_exception("attempt to evaluate a c2_binary_function stub"); return this->combine(*Left.get_ptr(), *Right.get_ptr(), x, yprime, yprime2); } /// \brief destructor releases ownership of member functions /// virtual ~c2_binary_function() { } protected: /// \brief construct the binary function /// \param combiner pointer to the function which actualy knows how to execute the binary /// \param left the c2_function to be used in the left side of the binary relation /// \param right the c2_function to be used in the right side of the binary relation c2_binary_function( float_type (*combiner)(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2), const c2_function &left, const c2_function &right) : c2_function(), combine(combiner), Left(left), Right(right), stub(false) { set_domain( (left.xmin() > right.xmin()) ? left.xmin() : right.xmin(), (left.xmax() < right.xmax()) ? left.xmax() : right.xmax() ); } /// \brief construct a 'stub' c2_binary_function, which provides access to the combine() function /// \note Do not evaluate a 'stub' ever. It is only used so that combine() can be called c2_binary_function( float_type (*combiner)(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2) ) : c2_function(), combine(combiner), Left(), Right(), stub(true) { } public: float_type (* const combine)(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2); protected: const c2_const_ptr Left, Right; /// \brief if true, we don't own any functions, we are just a source of a combining function. bool stub; }; /// \brief Create a very lightweight method to return a scalar multiple of another function. /// \ingroup containers \ingroup arithmetic_functions \ingroup parametric_functions /// /// The factory function c2_factory::scaled_function() creates *new c2_scaled_function_p template class c2_scaled_function_p : public c2_function { public: /// \brief construct the function with its scale factor. /// /// \param outer the function to be scaled /// \param scale the multiplicative scale factor c2_scaled_function_p(const c2_function &outer, float_type scale) : c2_function(), func(outer), yscale(scale) { } /// \brief set a new scale factor /// \param scale the new factor void reset(float_type scale) { yscale=scale; } /// \copydoc c2_function::value_with_derivatives /// /// provide our own value_with_derivatives which bypasses the combiner for quicker operation virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception) { float_type y=this->func->value_with_derivatives(x, yprime, yprime2); if(yprime) (*yprime)*=yscale; if(yprime2) (*yprime2)*=yscale; return y*yscale; } protected: c2_scaled_function_p() : func() {} // hide default constructor, since its use is almost always an error. /// \brief the scaling factor for the function const c2_const_ptr func; float_type yscale; }; /// \brief A container into which any other c2_function can be dropped. /// \ingroup containers /// It allows a function to be pre-evaluated at a point, and used at multiple places in an expression /// efficiently. If it is re-evaluated at the previous point, it returns the remembered values; /// otherwise, it re-evauates the function at the new point. /// /// The factory function c2_factory::cached_function() creates *new c2_cached_function_p template class c2_cached_function_p : public c2_function { public: /// \brief construct the container /// /// \param f the function to be cached c2_cached_function_p(const c2_function &f) : c2_function(), func(f), init(false) {} /// \copydoc c2_function::value_with_derivatives /// /// Checks to see if the function is being re-evaluated at the previous point, and /// returns remembered values if so. virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { if(!init || x != x0) { y=this->func->value_with_derivatives(x, &yp, &ypp); x0=x; init=true; } if(yprime) *yprime=yp; if(yprime2) *yprime2=ypp; return y; } protected: c2_cached_function_p() : func() {} // hide default constructor, since its use is almost always an error. const c2_const_ptr func; mutable bool init; mutable float_type x0, y, yp, ypp; }; /// \brief Provides function composition (nesting) /// \ingroup arithmetic_functions /// This allows evaluation of \a f(g(x)) where \a f and \a g are c2_function objects. /// /// This should always be constructed using \ref compose_operator "c2_function::operator()" template class c2_composed_function_p : public c2_binary_function { public: /// \brief construct \a outer( \a inner (x)) /// \note See c2_binary_function for discussion of ownership. /// \param outer the outer function /// \param inner the inner function c2_composed_function_p(const c2_function &outer, const c2_function &inner) : c2_binary_function(combine, outer, inner) { this->set_domain(inner.xmin(), inner.xmax()); } /// \brief Create a stub just for the combiner to avoid statics. c2_composed_function_p() : c2_binary_function(combine) {} /// \brief execute math necessary to do composition static float_type combine(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) { float_type y0, y1; if(yprime || yprime2) { float_type yp0, ypp0, yp1, ypp1; y0=right.value_with_derivatives(x, &yp0, &ypp0); y1=left.value_with_derivatives(y0, &yp1, &ypp1); if(yprime) *yprime=yp1*yp0; if(yprime2) *yprime2=ypp0*yp1+yp0*yp0*ypp1; } else { y0=right(x); y1=left(y0); } return y1; } }; /// \brief create a c2_function which is the sum of two other c2_function objects. /// \ingroup arithmetic_functions /// This should always be constructed using c2_function::operator+() template class c2_sum_p : public c2_binary_function { public: /// \brief construct \a left + \a right /// \param left the left function /// \param right the right function c2_sum_p(const c2_function &left, const c2_function &right) : c2_binary_function(combine, left, right) {} /// \brief Create a stub just for the combiner to avoid statics. c2_sum_p() : c2_binary_function(combine) {} ; // create a stub just for the combiner to avoid statics /// \brief execute math necessary to do addition static float_type combine(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) { float_type y0, y1; if(yprime || yprime2) { float_type yp0, ypp0, yp1, ypp1; y0=left.value_with_derivatives(x, &yp0, &ypp0); y1=right.value_with_derivatives(x, &yp1, &ypp1); if(yprime) *yprime=yp0+yp1; if(yprime2) *yprime2=ypp0+ypp1; } else { y0=left(x); y1=right(x); } return y0+y1; } }; /// \brief create a c2_function which is the difference of two other c2_functions. /// \ingroup arithmetic_functions /// This should always be constructed using c2_function::operator-() template class c2_diff_p : public c2_binary_function { public: /// \brief construct \a left - \a right /// \param left the left function /// \param right the right function c2_diff_p(const c2_function &left, const c2_function &right) : c2_binary_function(combine, left, right) {} /// \brief Create a stub just for the combiner to avoid statics. c2_diff_p() : c2_binary_function(combine) {} ; // create a stub just for the combiner to avoid statics /// \brief execute math necessary to do subtraction static float_type combine(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) { float_type y0, y1; if(yprime || yprime2) { float_type yp0, ypp0, yp1, ypp1; y0=left.value_with_derivatives(x, &yp0, &ypp0); y1=right.value_with_derivatives(x, &yp1, &ypp1); if(yprime) *yprime=yp0-yp1; if(yprime2) *yprime2=ypp0-ypp1; } else { y0=left(x); y1=right(x); } return y0-y1; } }; /// \brief create a c2_function which is the product of two other c2_functions. /// \ingroup arithmetic_functions /// This should always be constructed using c2_function::operator*() template class c2_product_p : public c2_binary_function { public: /// \brief construct \a left * \a right /// \param left the left function /// \param right the right function c2_product_p(const c2_function &left, const c2_function &right) : c2_binary_function(combine, left, right) {} /// \brief Create a stub just for the combiner to avoid statics. c2_product_p() : c2_binary_function(combine) {} ; // create a stub just for the combiner to avoid statics /// \brief execute math necessary to do multiplication static float_type combine(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) { float_type y0, y1; if(yprime || yprime2) { float_type yp0, ypp0, yp1, ypp1; y0=left.value_with_derivatives(x, &yp0, &ypp0); y1=right.value_with_derivatives(x, &yp1, &ypp1); if(yprime) *yprime=y1*yp0+y0*yp1; if(yprime2) *yprime2=ypp0*y1+2.0*yp0*yp1+ypp1*y0; } else { y0=left(x); y1=right(x); } return y0*y1; } }; /// \brief create a c2_function which is the ratio of two other c2_functions. /// \ingroup arithmetic_functions /// This should always be constructed using c2_function::operator/() template class c2_ratio_p : public c2_binary_function { public: /// \brief construct \a left / \a right /// \param left the left function /// \param right the right function c2_ratio_p(const c2_function &left, const c2_function &right) : c2_binary_function(combine, left, right) {} /// \brief Create a stub just for the combiner to avoid statics. c2_ratio_p() : c2_binary_function(combine) {} ; // create a stub just for the combiner to avoid statics /// \brief execute math necessary to do division static float_type combine(const c2_function &left, const c2_function &right, float_type x, float_type *yprime, float_type *yprime2) throw(c2_exception) { float_type y0, y1; if(yprime || yprime2) { float_type yp0, ypp0, yp1, ypp1; y0=left.value_with_derivatives(x, &yp0, &ypp0); y1=right.value_with_derivatives(x, &yp1, &ypp1); if(yprime) *yprime=(yp0*y1-y0*yp1)/(y1*y1); // first deriv of ratio if(yprime2) *yprime2=(y1*y1*ypp0+y0*(2*yp1*yp1-y1*ypp1)-2*y1*yp0*yp1)/(y1*y1*y1); } else { y0=left(x); y1=right(x); } return y0/y1; } }; /// \brief a c2_function which is constant /// \ingroup parametric_functions /// /// The factory function c2_factory::constant() creates *new c2_constant_p() template class c2_constant_p : public c2_function { public: c2_constant_p(float_type x) : c2_function(), value(x) {} void reset(float_type val) { value=val; } virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { if(yprime) *yprime=0; if(yprime2) *yprime2=0; return value; } private: float_type value; }; /// \brief a transformation of a coordinate, including an inverse /// \ingroup transforms template class c2_transformation { public: /// \brief initialize all our function pointers /// \param transformed true if this function is not the identity /// \param xin input X transform /// \param xinp input X transform derivative /// \param xinpp input X transform second derivative /// \param xout output X transform, which MUST be the inverse of \a xin c2_transformation(bool transformed, float_type (*xin)(float_type), float_type (*xinp)(float_type), float_type (*xinpp)(float_type), float_type (*xout)(float_type) ) : fTransformed(transformed), fHasStaticTransforms(true), pIn(xin), pInPrime(xinp), pInDPrime(xinpp), pOut(xout) { } /// \brief initialize all our function pointers so that only the (overridden) virtual functions can be called without an error /// \param transformed true if this function is nonlinear c2_transformation(bool transformed) : fTransformed(transformed), fHasStaticTransforms(false), pIn(report_error), pInPrime(report_error), pInDPrime(report_error), pOut(report_error) { } /// \brief the destructor virtual ~c2_transformation() { } /// \brief flag to indicate if this transform is not the identity const bool fTransformed; /// \brief flag to indicate if the static function pointers can be used for efficiency const bool fHasStaticTransforms; /// \note the pointers to functions allow highly optimized access when static functions are available. /// They are only used inside value_with_derivatives(), which is assumed to be the most critical routine. /// \brief non-virtual pointer to input X transform float_type (* const pIn)(float_type); /// \brief non-virtual pointer to input X transform derivative float_type (* const pInPrime)(float_type); /// \brief non-virtual pointer to input X transform second derivative float_type (* const pInDPrime)(float_type); /// \brief non-virtual pointer to output X transform float_type (* const pOut)(float_type); /// \brief virtual input X transform virtual float_type fIn(float_type x) const { return pIn(x); } /// \brief virtual input X transform derivative virtual float_type fInPrime(float_type x) const { return pInPrime(x); } /// \brief virtual input X transform second derivative virtual float_type fInDPrime(float_type x) const { return pInDPrime(x); } /// \brief virtual output X transform virtual float_type fOut(float_type x) const { return pOut(x); } protected: /// \brief utility function for unimplemented conversion static float_type report_error(float_type x) { throw c2_exception("use of improperly constructed axis transform"); return x; } /// \brief utility function f(x)=x useful in axis transforms static float_type ident(float_type x) { return x; } /// \brief utility function f(x)=1 useful in axis transforms static float_type one(float_type) { return 1; } /// \brief utility function f(x)=0 useful in axis transforms static float_type zero(float_type) { return 0; } /// \brief utility function f(x)=1/x useful in axis transforms static float_type recip(float_type x) { return 1.0/x; } /// \brief utility function f(x)=-1/x**2 useful in axis transforms static float_type recip_prime(float_type x) { return -1/(x*x); } /// \brief utility function f(x)=2/x**3 useful in axis transforms static float_type recip_prime2(float_type x) { return 2/(x*x*x); } }; /// \brief the identity transform /// \ingroup transforms template class c2_transformation_linear : public c2_transformation { public: /// \brief constructor c2_transformation_linear() : c2_transformation(false, this->ident, this->one, this->zero, this->ident) { } /// \brief destructor ~c2_transformation_linear() { } }; /// \brief log axis transform /// \ingroup transforms template class c2_transformation_log : public c2_transformation { public: /// \brief constructor c2_transformation_log() : c2_transformation(true, std::log, this->recip, this->recip_prime, std::exp) { } /// \brief destructor ~c2_transformation_log() { } }; /// \brief reciprocal axis transform /// \ingroup transforms template class c2_transformation_recip : public c2_transformation { public: /// \brief constructor c2_transformation_recip() : c2_transformation(true, this->recip, this->recip_prime, this->recip_prime2, this->recip) { } /// \brief destructor ~c2_transformation_recip() { } }; /// \brief a transformation of a function in and out of a coordinate space, using 2 c2_transformations /// /// This class is a container for two axis transforms, but also provides the critical evaluate() /// function which converts a result in internal coordinates (with derivatives) into the external representation /// \ingroup transforms template class c2_function_transformation { public: /// \brief construct this from two c2_transformation instances /// \param xx the X axis transform /// \param yy the Y axis transform c2_function_transformation( const c2_transformation &xx, const c2_transformation &yy) : isIdentity(!(xx.fTransformed || yy.fTransformed)), X(xx), Y(yy) { } /// \brief destructor virtual ~c2_function_transformation() { delete &X; delete &Y; } /// \brief evaluate the transformation from internal coordinates to external coordinates /// \param xraw the value of \a x in external cordinates at which the transform is taking place /// \param y the value of the function in internal coordinates /// \param yp0 the derivative in internal coordinates /// \param ypp0 the second derivative in internal coordinates /// \param [out] yprime pointer to the derivative, or NULL, in external coordinates /// \param [out] yprime2 pointer to the second derivative, or NULL, in external coordinates /// \return the value of the function in external coordinates virtual float_type evaluate(float_type xraw, float_type y, float_type yp0, float_type ypp0, float_type *yprime, float_type *yprime2) const; /// \brief flag indicating of the transform is the identity, and can be skipped for efficiency const bool isIdentity; /// \brief the X axis transform const c2_transformation &X; /// \brief the Y axis transform const c2_transformation &Y; }; /// \brief a transformation of a function in and out of lin-lin space /// /// \ingroup transforms template class c2_lin_lin_function_transformation : public c2_function_transformation { public: c2_lin_lin_function_transformation() : c2_function_transformation( *new c2_transformation_linear, *new c2_transformation_linear ) { } virtual ~c2_lin_lin_function_transformation() { } }; /// \brief a transformation of a function in and out of log-log space /// /// \ingroup transforms template class c2_log_log_function_transformation : public c2_function_transformation { public: c2_log_log_function_transformation() : c2_function_transformation( *new c2_transformation_log, *new c2_transformation_log ) { } virtual ~c2_log_log_function_transformation() { } }; /// \brief a transformation of a function in and out of lin-log space /// /// \ingroup transforms template class c2_lin_log_function_transformation : public c2_function_transformation { public: c2_lin_log_function_transformation() : c2_function_transformation( *new c2_transformation_linear, *new c2_transformation_log ) { } virtual ~c2_lin_log_function_transformation() { } }; /// \brief a transformation of a function in and out of log-lin space /// /// \ingroup transforms template class c2_log_lin_function_transformation : public c2_function_transformation { public: c2_log_lin_function_transformation() : c2_function_transformation( *new c2_transformation_log, *new c2_transformation_linear ) { } virtual ~c2_log_lin_function_transformation() { } }; /// \brief a transformation of a function in and out of Arrhenuis (1/x vs. log(y)) space /// /// \ingroup transforms template class c2_arrhenius_function_transformation : public c2_function_transformation { public: c2_arrhenius_function_transformation() : c2_function_transformation( *new c2_transformation_recip, *new c2_transformation_log ) { } virtual ~c2_arrhenius_function_transformation() { } }; /** \brief create a cubic spline interpolation of a set of (x,y) pairs \ingroup interpolators This is one of the main reasons for c2_function objects to exist. It provides support for cubic spline interpolation of data provides from tables of \a x, \a y pairs. It supports automatic, transparent linearization of the data before storing in its tables (through subclasses such as log_lin_interpolating_function, lin_log_interpolating_function, and log_log_interpolating_function) to permit very high accuracy representations of data which have a suitable structure. It provides utility functions LinearInterpolatingGrid() and LogLogInterpolatingGrid() to create grids for mapping other functions onto a arithmetic or geometric grid. In its simplest form, an untransformed cubic spline of a data set, using natural boundary conditions (vanishing second derivative), is created as: \n \code c2_ptr c2p; c2_factory c2; std::vector xvals(10), yvals(10); // < fill in xvals and yvals > c2p myfunc=c2.interpolating_function().load(xvals, yvals,true,0,true,0); // and it can be evaluated at a point for its value only by: double y=myfunc(x); // or it can be evaluated with its derivatives by double yprime, yprime2; double y=myfunc(x,&yprime, &yprime2); \endcode The factory function c2_factory::interpolating_function() creates *new interpolating_function_p() */ template class interpolating_function_p : public c2_function { public: /// \brief an empty linear-linear cubic-spline interpolating_function_p /// /// lots to say here, but see Numerical Recipes for a discussion of cubic splines. /// interpolating_function_p() : c2_function(), fTransform(*new c2_lin_lin_function_transformation) { } /// \brief an empty cubic-spline interpolating_function_p with a specific transform /// interpolating_function_p(const c2_function_transformation &transform) : c2_function(), fTransform(transform) { } /// \brief do the dirty work of constructing the spline from a function. /// \param x the list of abscissas. Must be either strictly increasing or strictly decreasing. /// Strictly increasing is preferred, as less memory is used since a copy is not required for the sampling grid. /// \param f the list of function values. /// \param lowerSlopeNatural if true, set y''(first point)=0, otherwise compute it from \a lowerSope /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false /// \param upperSlopeNatural if true, set y''(last point)=0, otherwise compute it from \a upperSope /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false /// \param splined if true (default), use cubic spline, if false, use linear interpolation. /// \return the same interpolating function, filled interpolating_function_p & load(const std::vector &x, const std::vector &f, bool lowerSlopeNatural, float_type lowerSlope, bool upperSlopeNatural, float_type upperSlope, bool splined=true ) throw(c2_exception); /// \brief do the dirty work of constructing the spline from a function. /// \param data std::vector of std::pairs of x,y. Will be sorted into x increasing order in place. /// \param lowerSlopeNatural if true, set y''(first point)=0, otherwise compute it from \a lowerSope /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false /// \param upperSlopeNatural if true, set y''(last point)=0, otherwise compute it from \a upperSope /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false /// \param splined if true (default), use cubic spline, if false, use linear interpolation. /// \return the same interpolating function, filled interpolating_function_p & load_pairs( std::vector > &data, bool lowerSlopeNatural, float_type lowerSlope, bool upperSlopeNatural, float_type upperSlope, bool splined=true ) throw(c2_exception); /// \brief do the dirty work of constructing the spline from a function. /// \param func a function without any requirement of valid derivatives to sample into an interpolating function. /// Very probably a c2_classic_function. /// \param xmin the lower bound of the region to sample /// \param xmax the upper bound of the region to sample /// \param abs_tol the maximum absolute error permitted when linearly interpolating the points. /// the real error will be much smaller, since this uses cubic splines at the end. /// \param rel_tol the maximum relative error permitted when linearly interpolating the points. /// the real error will be much smaller, since this uses cubic splines at the end. /// \param lowerSlopeNatural if true, set y'(first point) from 3-point parabola, otherwise compute it from \a lowerSope /// \param lowerSlope derivative of the function at the lower bound, used only if \a lowerSlopeNatural is false /// \param upperSlopeNatural if true, set y'(last point) from 3-point parabola, otherwise compute it from \a upperSope /// \param upperSlope derivative of the function at the upper bound, used only if \a upperSlopeNatural is false /// \return the same interpolating function, filled /// \note If the interpolator being filled has a log vertical axis, put the desired relative error in /// \a abs_tol, and 0 in \a rel_tol since the absolute error on the log of a function is the relative error /// on the function itself. interpolating_function_p & sample_function(const c2_function &func, float_type xmin, float_type xmax, float_type abs_tol, float_type rel_tol, bool lowerSlopeNatural, float_type lowerSlope, bool upperSlopeNatural, float_type upperSlope ) throw(c2_exception); /// \brief initialize from a grid of points and a c2_function (un-normalized) to an /// interpolator which, when evaluated with a uniform random variate on [0,1] returns random numbers /// distributed as the input function. /// \see \ref random_subsec "Arbitrary random generation" /// inverse_integrated_density starts with a probability density std::vector, generates the integral, /// and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values /// with a density distribution equal to the input distribution /// If the data are passed in reverse order (large X first), the integral is carried out from the big end. /// \param bincenters the positions at which to sample the function \a binheights /// \param binheights a function which describes the density of the random number distribution to be produced. /// \return an initialized interpolator, which /// if evaluated randomly with a uniform variate on [0,1] produces numbers /// distributed according to \a binheights interpolating_function_p & load_random_generator_function( const std::vector &bincenters, const c2_function &binheights) throw(c2_exception); /// \brief initialize from a grid of points and an std::vector of probability densities (un-normalized) to an /// interpolator which, when evaluated with a uniform random variate on [0,1] returns random numbers /// distributed as the input histogram. /// \see \ref random_subsec "Arbitrary random generation" /// inverse_integrated_density starts with a probability density std::vector, generates the integral, /// and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values /// with a density distribution equal to the input distribution /// If the data are passed in reverse order (large X first), the integral is carried out from the big end. /// \param bins if \a bins .size()==\a binheights .size(), the centers of the bins. \n /// if \a bins .size()==\a binheights .size()+1, the edges of the bins /// \param binheights a vector which describes the density of the random number distribution to be produced. /// Note density... the numbers in the bins are not counts, but counts/unit bin width. /// \return an initialized interpolator, which /// if evaluated randomly with a uniform variate on [0,1] produces numbers /// distributed according to \a binheights interpolating_function_p & load_random_generator_bins( const std::vector &bins, const std::vector &binheights) throw(c2_exception); virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception); /// \brief destructor virtual ~interpolating_function_p() { delete &fTransform; } /// \brief create a new, empty interpolating function of this type (virtual constructor) virtual interpolating_function_p &clone() const throw(c2_exception) { return *new interpolating_function_p(); } /// \brief retrieve copies of the x & y tables from which this was built /// /// This is often useful in the creation of new interpolating functions with transformed data. /// The vectors will have their sizes set correctly on return. /// \param [in, out] xvals the abscissas /// \param [in, out] yvals the ordinates void get_data(std::vector &xvals, std::vector &yvals) const throw() ; /// \brief enable extrapolation of the function below the tabulated data. /// /// This allows the interpolator to be extrapolated outside the bounds of the data, /// using whatever derivatives it already had at the lower bound. /// \param bound the abscissa to which the function should be extended. void set_lower_extrapolation(float_type bound); /// \brief enable extrapolation of the function above the tabulated data. /// /// This allows the interpolator to be extrapolated outside the bounds of the data, /// using whatever derivatives it already had at the upper bound. /// \param bound the abscissa to which the function should be extended. void set_upper_extrapolation(float_type bound); // these functions correctly combine the interpolating function with another interpolating function // preserving the X bounds and mapping functions of the host (left hand) function. /// \brief create a new interpolating_function_p which is the \a source /// function applied to every point in the interpolating tables /// /// This carefully manages the derivative of the composed function at the two ends. /// \param source the function to apply /// \return a new interpolating_function_p with the same mappings for x and y interpolating_function_p & unary_operator(const c2_function &source) const; /// \brief create a new interpolating_function_p which is the parent interpolating_function_p /// combined with \a rhs using \a combiner at every point in the interpolating tables /// /// This carefully manages the derivative of the composed function at the two ends. /// \param rhs the function to apply /// \param combining_stub a function which defines which binary operation to use. /// \return a new interpolating_function_p with the same mappings for x and y interpolating_function_p & binary_operator(const c2_function &rhs, const c2_binary_function *combining_stub ) const; /// \brief produce a newly resampled interpolating_function_p which is the specified sum. /// \param rhs the function to add, pointwise /// \return a new interpolating_function_p interpolating_function_p & add_pointwise (const c2_function &rhs) const { return binary_operator(rhs, new c2_sum_p()); } /// \brief produce a newly resampled interpolating_function_p which is the specified difference. /// \param rhs the function to subtract, pointwise /// \return a new interpolating_function_p interpolating_function_p & subtract_pointwise (const c2_function &rhs) const { return binary_operator(rhs, new c2_diff_p()); } /// \brief produce a newly resampled interpolating_function_p which is the specified product. /// \param rhs the function to multiply, pointwise /// \return a new interpolating_function_p interpolating_function_p & multiply_pointwise (const c2_function &rhs) const { return binary_operator(rhs, new c2_product_p()); } /// \brief produce a newly resampled interpolating_function_p which is the specified ratio. /// \param rhs the function to divide, pointwise /// \return a new interpolating_function_p interpolating_function_p & divide_pointwise (const c2_function &rhs) const { return binary_operator(rhs, new c2_ratio_p()); } /// \brief copy data from another interpolating function. This only makes sense if the source /// function has the same transforms as the destination. /// \param rhs interpolating_function_p to copy from void clone_data(const interpolating_function_p &rhs) { Xraw=rhs.Xraw; X=rhs.X; F=rhs.F; y2=rhs.y2; set_sampling_grid_pointer(Xraw); } const c2_function_transformation &fTransform; protected: /// \brief create the spline coefficients void spline( bool lowerSlopeNatural, float_type lowerSlope, bool upperSlopeNatural, float_type upperSlope ) throw(c2_exception); // This is for sorting the data. It must be static if it's going to be a class member. static bool comp_pair(std::pair const &i, std::pair const &j) {return i.first Xraw, X, F, y2; c2_const_ptr sampler_function; bool xInverted; mutable size_t lastKLow; }; /// \brief A spline with X transformed into log space. /// \ingroup interpolators /// Most useful for functions looking like y=log(x) or any other function with a huge X dynamic range, /// and a slowly varying Y. /// /// The factory function c2_factory::log_lin_interpolating_function() creates *new log_lin_interpolating_function_p() template class log_lin_interpolating_function_p : public interpolating_function_p { public: /// \brief an empty log-linear cubic-spline interpolating_function_p /// log_lin_interpolating_function_p() : interpolating_function_p(*new c2_log_lin_function_transformation) { } /// \brief create a new, empty interpolating function of this type (virtual constructor) virtual interpolating_function_p &clone() const throw(c2_exception) { return *new log_lin_interpolating_function_p(); } }; /// \brief A spline with Y transformed into log space. /// \ingroup interpolators /// Most useful for functions looking like y=exp(x) /// /// The factory function c2_factory::lin_log_interpolating_function() creates *new lin_log_interpolating_function_p() template class lin_log_interpolating_function_p : public interpolating_function_p { public: /// \brief an empty linear-log cubic-spline interpolating_function_p /// lin_log_interpolating_function_p() : interpolating_function_p(*new c2_lin_log_function_transformation) { } /// \brief create a new, empty interpolating function of this type (virtual constructor) virtual interpolating_function_p &clone() const throw(c2_exception) { return *new lin_log_interpolating_function_p(); } }; /// \brief A spline with X and Y transformed into log space. /// \ingroup interpolators /// Most useful for functions looking like y=x^n or any other function with a huge X and Y dynamic range. /// /// The factory function c2_factory::log_log_interpolating_function() creates *new log_log_interpolating_function_p() template class log_log_interpolating_function_p : public interpolating_function_p { public: /// \brief an empty log-log cubic-spline interpolating_function_p /// log_log_interpolating_function_p() : interpolating_function_p(*new c2_log_log_function_transformation) { } /// \brief create a new, empty interpolating function of this type (virtual constructor) virtual interpolating_function_p &clone() const throw(c2_exception) { return *new log_log_interpolating_function_p(); } }; /// \brief A spline with X in reciprocal space and Y transformed in log space. /// \ingroup interpolators /// Most useful for thermodynamic types of data where Y is roughly A*exp(-B/x). /// Typical examples are reaction rate data, and thermistor calibration data. /// /// The factory function c2_factory::arrhenius_interpolating_function() creates *new arrhenius_interpolating_function_p() template class arrhenius_interpolating_function_p : public interpolating_function_p { public: /// \brief an empty arrhenius cubic-spline interpolating_function_p /// arrhenius_interpolating_function_p() : interpolating_function_p(*new c2_arrhenius_function_transformation) { } /// \brief create a new, empty interpolating function of this type (virtual constructor) virtual interpolating_function_p &clone() const throw(c2_exception) { return *new arrhenius_interpolating_function_p(); } }; /// \brief compute sin(x) with its derivatives. /// \ingroup math_functions /// /// The factory function c2_factory::sin() creates *new c2_sin_p template class c2_sin_p : public c2_function { public: /// \brief constructor. c2_sin_p() : c2_function() {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type q=std::sin(x); if(yprime) *yprime=std::cos(x); if(yprime2) *yprime2=-q; return q; } /// \brief return a grid dynamically, suitable for use with trig functions with period 2*pi /// \param xmin the lower bound for the grid /// \param xmax upper bound for the grid /// \param [in, out] grid the sampling grid. virtual void get_sampling_grid(float_type xmin, float_type xmax, std::vector &grid) const; }; /// \brief compute cos(x) with its derivatives. /// \ingroup math_functions /// /// The factory function c2_factory::cos() creates *new c2_cos_p template class c2_cos_p : public c2_sin_p { public: /// \brief constructor. c2_cos_p() : c2_sin_p() {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type q=std::cos(x); if(yprime) *yprime=-std::sin(x); if(yprime2) *yprime2=-q; return q; } }; /// \brief compute tan(x) with its derivatives. /// \ingroup math_functions /// /// The factory function c2_factory::tan() creates *new c2_tan_p template class c2_tan_p : public c2_function { public: /// \brief constructor. c2_tan_p() : c2_function() {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type c=std::cos(x), s=std::sin(x); float_type t=s/c; float_type yp=1/(c*c); if(yprime) *yprime=yp; if(yprime2) *yprime2=2*t*yp; return t; } }; /// \brief compute log(x) with its derivatives. /// \ingroup math_functions /// /// The factory function c2_factory::log() creates *new c2_log_p template class c2_log_p : public c2_function { public: /// \brief constructor. c2_log_p() : c2_function() {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { if(yprime) *yprime=1.0/x; if(yprime2) *yprime2=-1.0/(x*x); return std::log(x); } }; /// \brief compute exp(x) with its derivatives. /// \ingroup math_functions /// /// The factory function c2_factory::exp() creates *new c2_exp_p template class c2_exp_p : public c2_function { public: /// \brief constructor. c2_exp_p() : c2_function() {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type q=std::exp(x); if(yprime) *yprime=q; if(yprime2) *yprime2=q; return q; } }; /// \brief compute sqrt(x) with its derivatives. /// \ingroup math_functions /// /// The factory function c2_factory::sqrt() creates *new c2_sqrt_p() template class c2_sqrt_p : public c2_function { public: /// \brief constructor. c2_sqrt_p() : c2_function() {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type q=std::sqrt(x); if(yprime) *yprime=0.5/q; if(yprime2) *yprime2=-0.25/(x*q); return q; } }; /// \brief compute scale/x with its derivatives. /// \ingroup parametric_functions /// /// The factory function c2_factory::recip() creates *new c2_recip_p template class c2_recip_p : public c2_function { public: /// \brief constructor. c2_recip_p(float_type scale) : c2_function(), rscale(scale) {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type q=1.0/x; float_type y=rscale*q; if(yprime) *yprime=-y*q; if(yprime2) *yprime2=2*y*q*q; return y; } /// \brief reset the scale factor /// \param scale the new numerator void reset(float_type scale) { rscale=scale; } private: float_type rscale; }; /// \brief compute x with its derivatives. /// \ingroup math_functions /// /// The factory function c2_factory::identity() creates *new c2_identity_p template class c2_identity_p : public c2_function { public: /// \brief constructor. c2_identity_p() : c2_function() {} virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { if(yprime) *yprime=1.0; if(yprime2) *yprime2=0; return x; } }; /** \brief create a linear mapping of another function \ingroup parametric_functions for example, given a c2_function \a f \code c2_function &F=c2_linear(1.2, 2.0, 3.0)(f); \endcode produces a new c2_function F=2.0+3.0*(\a f - 1.2) The factory function c2_factory::linear() creates *new c2_linear_p */ template class c2_linear_p : public c2_function { public: /// \brief Construct the operator f=y0 + slope * (x-x0) /// \param x0 the x offset /// \param y0 the y-intercept i.e. f(x0) /// \param slope the slope of the mapping c2_linear_p(float_type x0, float_type y0, float_type slope) : c2_function(), xint(x0), intercept(y0), m(slope) {} /// \brief Change the slope and intercepts after construction. /// \param x0 the x offset /// \param y0 the y-intercept /// \param slope the slope of the mapping void reset(float_type x0, float_type y0, float_type slope) { xint=x0; intercept=y0; m=slope; } virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { if(yprime) *yprime=m; if(yprime2) *yprime2=0; return m*(x-xint)+intercept; } private: float_type xint, intercept, m; protected: c2_linear_p() {} // do not allow naked construction... it is usually an accident. }; /** \brief create a quadratic mapping of another function \ingroup parametric_functions for example, given a c2_function \a f \code c2_function &F=c2_quadratic(1.2, 2.0, 3.0, 4.0)(f); \endcode produces a new c2_function F=2.0 + 3.0*(f-1.2) + 4.0*(f-1.2)^2 note that the parameters are overdetermined, but allows the flexibility of two different representations The factory function c2_factory::quadratic() creates *new c2_quadratic_p */ template class c2_quadratic_p : public c2_function { public: /// \brief Construct the operator /// \param x0 the center around which the powers are computed /// \param y0 the value of the function at \a x = \a x0 /// \param xcoef the scale on the (\a x - \a x0) term /// \param x2coef the scale on the (\a x - \a x0)^2 term c2_quadratic_p(float_type x0, float_type y0, float_type xcoef, float_type x2coef) : c2_function(), intercept(y0), center(x0), a(x2coef), b(xcoef) {} /// \brief Modify the coefficients after construction /// \param x0 the new center around which the powers are computed /// \param y0 the new value of the function at \a x = \a x0 /// \param xcoef the new scale on the (\a x - \a x0) term /// \param x2coef the new scale on the (\a x - \a x0)^2 term void reset(float_type x0, float_type y0, float_type xcoef, float_type x2coef) { intercept=y0; center=x0; a=x2coef; b=xcoef; } virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type dx=x-center; if(yprime) *yprime=2*a*dx+b; if(yprime2) *yprime2=2*a; return a*dx*dx+b*dx+intercept; } private: float_type intercept, center, a, b; protected: c2_quadratic_p() {} // do not allow naked construction... it is usually an accident. }; /** \brief create a power law mapping of another function \ingroup parametric_functions for example, given a c2_function \a f \code c2_power_law_p PLaw(1.2, 2.5); c2_composed_function_p &F=PLaw(f); \endcode produces a new c2_function F=1.2 * f^2.5 The factory function c2_factory::power_law() creates *new c2_power_law_p */ template class c2_power_law_p : public c2_function { public: /// \brief Construct the operator /// \param scale the multipler /// \param power the exponent c2_power_law_p(float_type scale, float_type power) : c2_function(), a(scale), b(power) {} /// \brief Modify the mapping after construction /// \param scale the new multipler /// \param power the new exponent void reset(float_type scale, float_type power) { a=scale; b=power; } virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception) { float_type q=a*std::pow(x,b-2); if(yprime) *yprime=b*q*x; if(yprime2) *yprime2=b*(b-1)*q; return q*x*x; } private: float_type a, b; protected: c2_power_law_p() {} // do not allow naked construction... it is usually an accident. }; /** \brief create the formal inverse function of another function \ingroup containers for example, given a c2_function \a f \code c2_inverse_function inv(f); a=f(x); x1=inv(a); \endcode will return x1=x to machine precision. The important part of this is that the resulting function is a first-class c2_function, so it knows its derivatives, too, unlike the case of a simple root-finding inverse. This means it can be integrated (for example) quite efficiently. \see \ref combined_inversion_hinting_sampling The factory function c2_factory::inverse_function() creates *new c2_inverse_function_p */ template class c2_inverse_function_p : public c2_function { public: /// \brief Construct the operator /// \param source the function to be inverted c2_inverse_function_p(const c2_function &source); virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw(c2_exception); /// \brief give the function a hint as to where to look for its inverse /// \param hint the likely value of the inverse, which defaults to whatever the evaluation returned. void set_start_hint(float_type hint) const { start_hint=hint; } /// \brief get the starting hint. /// /// This is virtual so if there is a better way, this can be easily overridden. /// It is used in value_with_derivatives() to guess where to start the root finder. /// \param x the abscissa for which an estimate is needed virtual float_type get_start_hint(float_type x) const { return hinting_function.valid()? hinting_function(x) : start_hint; } /// \brief set or unset the approximate function used to start the root finder /// \anchor set_hinting_function_discussion /// A hinting function is mostly useful if the evaluation of this inverse is /// going to be carried out in very non-local order, so the root finder has to start over /// for each step. If most evaluations are going to be made in fairly localized clusters (scanning /// through the function, for example), the default mechanism used (which just remembers the last point) /// is almost certainly faster. /// /// Typically, the hinting function is likely to be set up by creating the inverse function, /// and then adaptively sampling an interpolating function from it, and then using the result /// to hint it. Another way, if the parent function is already an interpolating function, is just to create /// a version of the parent with the x & y coordinates reversed. /// /// \see \ref combined_inversion_hinting_sampling /// /// \param hint_func the function that is an approximate inverse of the parent of this inverse_function void set_hinting_function(const c2_function *hint_func) { hinting_function.set_function(hint_func); } /// \brief set the hinting function from a pointer. /// /// See \ref set_hinting_function_discussion "discussion" /// \param hint_func the container holding the function void set_hinting_function(const c2_const_ptr hint_func) { hinting_function=hint_func; } protected: c2_inverse_function_p() {} // do not allow naked construction... it is usually an accident. mutable float_type start_hint; const c2_const_ptr func; c2_const_ptr hinting_function; }; /** \brief An interpolating_function_p which is the cumulative integral of a histogram. \ingroup interpolators Note than binedges should be one element longer than binheights, since the lower & upper edges are specified. Note that this is a malformed spline, since the second derivatives are all zero, so it has less continuity. Also, note that the bin edges can be given in backwards order to generate the reversed accumulation (starting at the high end) */ template class accumulated_histogram : public interpolating_function_p { public: /// \brief Construct the integrated histogram /// \param binedges the edges of the bins in \a binheights. It should have one more element than \a binheights /// \param binheights the number of counts in each bin. /// \param normalize if true, normalize integral to 1 /// \param inverse_function if true, drop zero channels, and return inverse function for random generation /// \param drop_zeros eliminate null bins before integrating, so integral is strictly monotonic. accumulated_histogram(const std::vectorbinedges, const std::vector binheights, bool normalize=false, bool inverse_function=false, bool drop_zeros=true); }; /** \anchor inverse_integrated_density_bins \brief construct from a grid of points and an std::vector of probability densities (un-normalized) \see \ref random_subsec "Arbitrary random generation" \ingroup interpolators inverse_integrated_density starts with a probability density std::vector, generates the integral, and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values with a density distribution equal to the input distribution If the data are passed in reverse order (large X first), the integral is carried out from the big end. \param bins if \a bins .size()==\a binheights .size(), the centers of the bins. \n if \a bins .size()==\a binheights .size()+1, the edges of the bins \param binheights a vector which describes the density of the random number distribution to be produced. Note density... the numbers in the bins are not counts, but counts/unit bin width. \return an interpolating_function_p of the type requested in the template which, if evaluated randomly with a uniform variate on [0,1] produces numbers distributed according to \a binheights */ template interpolating_function_p & inverse_integrated_density_bins( const std::vector &bins, const std::vector &binheights) throw(c2_exception); /** \anchor inverse_integrated_density_function \brief construct from a grid of points and a c2_function of probability densities (un-normalized) \see \ref random_subsec "Arbitrary random generation" \ingroup interpolators inverse_integrated_density starts with a probability density std::vector, generates the integral, and generates an interpolating_function_p of the inverse function which, when evaluated using a uniform random on [0,1] returns values with a density distribution equal to the input distribution If the data are passed in reverse order (large X first), the integral is carried out from the big end. \param bincenters the centers of the bins. \param binheights a c2_function which describes the density of the random number distribution to be produced. \return an interpolating_function_p of the type requested in the template which, if evaluated randomly with a uniform variate on [0,1] produces numbers distributed according to \a binheights */ template interpolating_function_p & inverse_integrated_density_function( const std::vector &bincenters, const c2_function &binheights) throw(c2_exception); /// \brief create a c2_function which smoothly connects two other c2_functions. /// \ingroup parametric_functions /// This takes two points and generates a polynomial which matches two c2_function arguments /// at those two points, with two derivatives at each point, and an arbitrary value at the center of the /// region. It is useful for splicing together functions over rough spots (0/0, for example). /// /// If \a auto_center is true, the value at the midpoint is computed so that the resulting polynomial is /// of order 5. If \a auto_center is false, the value \a y1 is used at the midpoint, resulting in a /// polynomial of order 6. /// /// This is usually used in conjunction with c2_piecewise_function_p to assemble an apparently seamless /// function from a series of segments. /// \see \ref piecewise_applications_subsec "Sample Applications" and \ref c2_function::adaptively_sample() "Adaptive sampling" /// /// The factory function c2_factory::connector_function() creates *new c2_connector_function_p template class c2_connector_function_p : public c2_function { public: /// \brief construct the container from two functions /// \param x0 the point at which to match \a f1 and its derivatives /// \param f0 the function on the left side to be connected /// \param x2 the point at which to match \a f2 and its derivatives /// \param f2 the function on the right side to be connected /// \param auto_center if true, no midpoint value is specified. If false, match the value \a y1 at the midpoint /// \param y1 the value to match at the midpoint, if \a auto_center is false /// \return a c2_function with domain (\a x0,\a x2) which smoothly connects \a f0(x0) and \a f2(x2) c2_connector_function_p(float_type x0, const c2_function &f0, float_type x2, const c2_function &f2, bool auto_center, float_type y1); /// \brief construct the container from numerical values /// \param x0 the position of the left edge /// \param y0 the function derivative on the left boundary /// \param yp0 the function second derivative on the left boundary /// \param ypp0 the function value on the left boundary /// \param x2 the position of the right edge /// \param y2 the function derivative on the right boundary /// \param yp2 the function second derivative on the right boundary /// \param ypp2 the function value on the right boundary /// \param auto_center if true, no midpoint value is specified. If false, match the value \a y1 at the midpoint /// \param y1 the value to match at the midpoint, if \a auto_center is false /// \return a c2_function with domain (\a x0,\a x2) which smoothly connects the points described /// \anchor c2_connector_raw_init_docs c2_connector_function_p( float_type x0, float_type y0, float_type yp0, float_type ypp0, float_type x2, float_type y2, float_type yp2, float_type ypp2, bool auto_center, float_type y1); /// \brief construct the container from c2_fblock objects /// \param fb0 the left edge /// \param fb2 the right edge /// \param auto_center if true, no midpoint value is specified. If false, match the value \a y1 at the midpoint /// \param y1 the value to match at the midpoint, if \a auto_center is false /// \return a c2_function with domain (\a fb0.x,\a fb2.x) which smoothly connects \a fb0 and \a fb2 c2_connector_function_p( const c2_fblock &fb0, const c2_fblock &fb2, bool auto_center, float_type y1); /// \brief destructor virtual ~c2_connector_function_p(); virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception); protected: /// \brief fill container numerically void init( const c2_fblock &fb0, const c2_fblock &fb2, bool auto_center, float_type y1); float_type fhinv, fy1, fa, fb, fc, fd, fe, ff; }; /// \brief create a c2_function which is a piecewise assembly of other c2_functions. /// \ingroup containers /// The functions must have increasing, non-overlapping domains. Any empty space /// between functions will be filled with a linear interpolation. /// /// \note If you want a smooth connection, instead of the default linear interpolation, /// create a c2_connector_function_p to bridge the gap. The linear interpolation is intended /// to be a barely intelligent bridge, and may never get used by anyone. /// /// \note The creation of the container results in the creation of an explicit sampling grid. /// If this is used with functions with a large domain, or which generate very dense sampling grids, /// it could eat a lot of memory. Do not abuse this by using functions which can generate gigantic grids. /// /// \see \ref piecewise_applications_subsec "Sample Applications" \n /// c2_plugin_function_p page \n /// c2_connector_function_p page \n /// \ref c2_function::adaptively_sample() "Adaptive sampling" /// /// The factory function c2_factory::piecewise_function() creates *new c2_piecewise_function_p template class c2_piecewise_function_p : public c2_function { public: /// \brief construct the container c2_piecewise_function_p(); /// \brief destructor virtual ~c2_piecewise_function_p(); virtual float_type value_with_derivatives(float_type x, float_type *yprime, float_type *yprime2) const throw (c2_exception); /// \brief append a new function to the sequence /// /// This takes a c2_function, and appends it onto the end of the piecewise collection. /// The domain of the function (which MUST be set) specifies the place it will be used in /// the final function. If the domain exactly abuts the domain of the previous function, it /// will be directly attached. If there is a gap, the gap will be filled in by linear interpolation. /// \param func a c2_function with a defined domain to be appended to the collection void append_function(const c2_function &func) throw (c2_exception); protected: std::vector > functions; mutable int lastKLow; }; #include "c2_function.icc" #endif