Squashed 'boost/' content from commit b4feb19f2

git-subtree-dir: boost
git-subtree-split: b4feb19f287ee92d87a9624b5d36b7cf46aeadeb
This commit is contained in:
Bill Somerville
2018-06-09 21:48:32 +01:00
commit 4ebe6417a5
12444 changed files with 2327021 additions and 0 deletions
+177
View File
@@ -0,0 +1,177 @@
[section:cf Continued Fraction Evaluation]
[h4 Synopsis]
``
#include <boost/math/tools/fraction.hpp>
``
namespace boost{ namespace math{ namespace tools{
template <class Gen, class U>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_b(Gen& g, const U& tolerance, boost::uintmax_t& max_terms)
template <class Gen, class U>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_b(Gen& g, const U& tolerance)
template <class Gen, class U>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_a(Gen& g, const U& tolerance, boost::uintmax_t& max_terms)
template <class Gen, class U>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_a(Gen& g, const U& tolerance)
//
// These interfaces are present for legacy reasons, and are now deprecated:
//
template <class Gen>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_b(Gen& g, int bits);
template <class Gen>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_b(Gen& g, int bits, boost::uintmax_t& max_terms);
template <class Gen>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_a(Gen& g, int bits);
template <class Gen>
typename detail::fraction_traits<Gen>::result_type
continued_fraction_a(Gen& g, int bits, boost::uintmax_t& max_terms);
}}} // namespaces
[h4 Description]
[@http://en.wikipedia.org/wiki/Continued_fraction Continued fractions are a common method of approximation. ]
These functions all evaluate the continued fraction described by the /generator/
type argument. The functions with an "_a" suffix evaluate the fraction:
[equation fraction2]
and those with a "_b" suffix evaluate the fraction:
[equation fraction1]
This latter form is somewhat more natural in that it corresponds with the usual
definition of a continued fraction, but note that the first /a/ value returned by
the generator is discarded. Further, often the first /a/ and /b/ values in a
continued fraction have different defining equations to the remaining terms, which
may make the "_a" suffixed form more appropriate.
The generator type should be a function object which supports the following
operations:
[table
[[Expression] [Description]]
[[Gen::result_type] [The type that is the result of invoking operator().
This can be either an arithmetic type, or a std::pair<> of arithmetic types.]]
[[g()] [Returns an object of type Gen::result_type.
Each time this operator is called then the next pair of /a/ and /b/
values is returned. Or, if result_type is an arithmetic type,
then the next /b/ value is returned and all the /a/ values
are assumed to 1.]]
]
In all the continued fraction evaluation functions the /tolerance/ parameter is the
precision desired in the result, evaluation of the fraction will
continue until the last term evaluated leaves the relative error in the result
less than /tolerance/. The deprecated interfaces take a number of digits precision
here, internally they just convert this to a tolerance and forward call.
If the optional /max_terms/ parameter is specified then no more than /max_terms/
calls to the generator will be made, and on output,
/max_terms/ will be set to actual number of
calls made. This facility is particularly useful when profiling a continued
fraction for convergence.
[h4 Implementation]
Internally these algorithms all use the modified Lentz algorithm: refer to
Numeric Recipes in C++, W. H. Press et all, chapter 5,
(especially 5.2 Evaluation of continued fractions, p 175 - 179)
for more information, also
Lentz, W.J. 1976, Applied Optics, vol. 15, pp. 668-671.
[h4 Examples]
The [@http://en.wikipedia.org/wiki/Golden_ratio golden ratio phi = 1.618033989...]
can be computed from the simplest continued fraction of all:
[equation fraction3]
We begin by defining a generator function:
template <class T>
struct golden_ratio_fraction
{
typedef T result_type;
result_type operator()
{
return 1;
}
};
The golden ratio can then be computed to double precision using:
continued_fraction_a(
golden_ratio_fraction<double>(),
std::numeric_limits<double>::epsilon());
It's more usual though to have to define both the /a/'s and the /b/'s
when evaluating special functions by continued fractions, for example
the tan function is defined by:
[equation fraction4]
So its generator object would look like:
template <class T>
struct tan_fraction
{
private:
T a, b;
public:
tan_fraction(T v)
: a(-v*v), b(-1)
{}
typedef std::pair<T,T> result_type;
std::pair<T,T> operator()()
{
b += 2;
return std::make_pair(a, b);
}
};
Notice that if the continuant is subtracted from the /b/ terms,
as is the case here, then all the /a/ terms returned by the generator
will be negative. The tangent function can now be evaluated using:
template <class T>
T tan(T a)
{
tan_fraction<T> fract(a);
return a / continued_fraction_b(fract, std::numeric_limits<T>::epsilon());
}
Notice that this time we're using the "_b" suffixed version to evaluate
the fraction: we're removing the leading /a/ term during fraction evaluation
as it's different from all the others.
[endsect][/section:cf Continued Fraction Evaluation]
[/
Copyright 2006 John Maddock and Paul A. Bristow.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
@@ -0,0 +1,22 @@
[section:internals_overview Overview]
This section contains internal utilities used by the library's implementation
along with tools used in development and testing. These tools have
limitied documentation, but now have quite stable interfaces
and may also be useful outside Boost.Math.
There is no doubt that these components can be improved, but they are also
largely incidental to the main purpose of this library.
These tools are designed to "just get the job done", and receive minimal
documentation here, in the hopes that they will help stimulate further
submissions to this library.
[endsect] [/section:internals_overview Overview]
[/
Copyright 2006, 2010, 2013, 2014 John Maddock and Paul A. Bristow.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
+167
View File
@@ -0,0 +1,167 @@
[section:minimax Minimax Approximations and the Remez Algorithm]
The directory libs/math/minimax contains a command line driven
program for the generation of minimax approximations using the Remez
algorithm. Both polynomial and rational approximations are supported,
although the latter are tricky to converge: it is not uncommon for
convergence of rational forms to fail. No such limitations are present
for polynomial approximations which should always converge smoothly.
It's worth stressing that developing rational approximations to functions
is often not an easy task, and one to which many books have been devoted.
To use this tool, you will need to have a reasonable grasp of what the Remez
algorithm is, and the general form of the approximation you want to achieve.
Unless you already familar with the Remez method,
you should first read the [link math_toolkit.remez
brief background article explaining the principles behind the
Remez algorithm].
The program consists of two parts:
[variablelist
[[main.cpp][Contains the command line parser, and all the calls to the Remez code.]]
[[f.cpp][Contains the function to approximate.]]
]
Therefore to use this tool, you must modify f.cpp to return the function to
approximate. The tools supports multiple function approximations within
the same compiled program: each as a separate variant:
NTL::RR f(const NTL::RR& x, int variant);
Returns the value of the function /variant/ at point /x/. So if you
wish you can just add the function to approximate as a new variant
after the existing examples.
In addition to those two files, the program needs to be linked to
a [link math_toolkit.high_precision.use_ntl patched NTL library to compile].
Note that the function /f/ must return the rational part of the
approximation: for example if you are approximating a function
/f(x)/ then it is quite common to use:
f(x) = g(x)(Y + R(x))
where /g(x)/ is the dominant part of /f(x)/, /Y/ is some constant, and
/R(x)/ is the rational approximation part, usually optimised for a low
absolute error compared to |Y|.
In this case you would define /f/ to return ['f(x)/g(x)] and then set the
y-offset of the approximation to /Y/ (see command line options below).
Many other forms are possible, but in all cases the objective is to
split /f(x)/ into a dominant part that you can evaluate easily using
standard math functions, and a smooth and slowly changing rational approximation
part. Refer to your favourite textbook for more examples.
Command line options for the program are as follows:
[variablelist
[[variant N][Sets the current function variant to N. This allows multiple functions
that are to be approximated to be compiled into the same executable.
Defaults to 0.]]
[[range a b][Sets the domain for the approximation to the range \[a,b\], defaults
to \[0,1\].]]
[[relative][Sets the Remez code to optimise for relative error. This is the default
at program startup. Note that relative error can only be used
if f(x) has no roots over the range being optimised.]]
[[absolute][Sets the Remez code to optimise for absolute error.]]
[[pin \[true|false\]]["Pins" the code so that the rational approximation
passes through the origin. Obviously only set this to
/true/ if R(0) must be zero. This is typically used when
trying to preserve a root at \[0,0\] while also optimising
for relative error.]]
[[order N D][Sets the order of the approximation to /N/ in the numerator and /D/
in the denominator. If /D/ is zero then the result will be a polynomial
approximation. There will be N+D+2 coefficients in total, the first
coefficient of the numerator is zero if /pin/ was set to true, and the
first coefficient of the denominator is always one.]]
[[working-precision N][Sets the working precision of NTL::RR to /N/ binary digits. Defaults to 250.]]
[[target-precision N][Sets the precision of printed output to /N/ binary digits:
set to the same number of digits as the type that will be used to
evaluate the approximation. Defaults to 53 (for double precision).]]
[[skew val]["Skews" the initial interpolated control points towards one
end or the other of the range. Positive values skew the
initial control points towards the left hand side of the
range, and negative values towards the right hand side.
If an approximation won't converge (a common situation)
try adjusting the skew parameter until the first step yields
the smallest possible error. /val/ should be in the range
\[-100,+100\], the default is zero.]]
[[brake val][Sets a brake on each step so that the change in the
control points is braked by /val%/. Defaults to 50,
try a higher value if an approximation won't converge,
or a lower value to get speedier convergence.]]
[[x-offset val][Sets the x-offset to /val/: the approximation will
be generated for `f(S * (x + X)) + Y` where /X/ is the
x-offset, /S/ is the x-scale
and /Y/ is the y-offset. Defaults to zero. To avoid
rounding errors, take care to specify a value that can
be exactly represented as a floating point number.]]
[[x-scale val][Sets the x-scale to /val/: the approximation will
be generated for `f(S * (x + X)) + Y` where /S/ is the
x-scale, /X/ is the x-offset
and /Y/ is the y-offset. Defaults to one. To avoid
rounding errors, take care to specify a value that can
be exactly represented as a floating point number.]]
[[y-offset val][Sets the y-offset to /val/: the approximation will
be generated for `f(S * (x + X)) + Y` where /X/
is the x-offset, /S/ is the x-scale
and /Y/ is the y-offset. Defaults to zero. To avoid
rounding errors, take care to specify a value that can
be exactly represented as a floating point number.]]
[[y-offset auto][Sets the y-offset to the average value of f(x)
evaluated at the two endpoints of the range plus the midpoint
of the range. The calculated value is deliberately truncated
to /float/ precision (and should be stored as a /float/
in your code). The approximation will
be generated for `f(x + X) + Y` where /X/ is the x-offset
and /Y/ is the y-offset. Defaults to zero.]]
[[graph N][Prints N evaluations of f(x) at evenly spaced points over the
range being optimised. If unspecified then /N/ defaults
to 3. Use to check that f(x) is indeed smooth over the range
of interest.]]
[[step N][Performs /N/ steps, or one step if /N/ is unspecified.
After each step prints: the peek error at the extrema of
the error function of the approximation,
the theoretical error term solved for on the last step,
and the maximum relative change in the location of the
Chebyshev control points. The approximation is converged on the
minimax solution when the two error terms are (approximately)
equal, and the change in the control points has decreased to
a suitably small value.]]
[[test \[float|double|long\]][Tests the current approximation at float,
double, or long double precision. Useful to check for rounding
errors in evaluating the approximation at fixed precision.
Tests are conducted at the extrema of the error function of the
approximation, and at the zeros of the error function.]]
[[test \[float|double|long\] N] [Tests the current approximation at float,
double, or long double precision. Useful to check for rounding
errors in evaluating the approximation at fixed precision.
Tests are conducted at N evenly spaced points over the range
of the approximation. If none of \[float|double|long\] are specified
then tests using NTL::RR, this can be used to obtain the error
function of the approximation.]]
[[rescale a b][Takes the current Chebeshev control points, and rescales them
over a new interval \[a,b\]. Sometimes this can be used to obtain
starting control points for an approximation that can not otherwise be
converged.]]
[[rotate][Moves one term from the numerator to the denominator, but keeps the
Chebyshev control points the same. Sometimes this can be used to obtain
starting control points for an approximation that can not otherwise be
converged.]]
[[info][Prints out the current approximation: the location of the zeros of the
error function, the location of the Chebyshev control points, the
x and y offsets, and of course the coefficients of the polynomials.]]
]
[endsect][/section:minimax Minimax Approximations and the Remez Algorithm]
[/
Copyright 2006 John Maddock and Paul A. Bristow.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
+179
View File
@@ -0,0 +1,179 @@
[section:polynomials Polynomials]
[h4 Synopsis]
``
#include <boost/math/tools/polynomial.hpp>
``
namespace boost { namespace math {
namespace tools {
template <class T>
class polynomial
{
public:
// typedefs:
typedef typename std::vector<T>::value_type value_type;
typedef typename std::vector<T>::size_type size_type;
// construct:
polynomial(){}
template <class U>
polynomial(const U* data, unsigned order);
template <class I>
polynomial(I first, I last);
template <class U>
explicit polynomial(const U& point);
polynomial(std::initializer_list<T> l); // C++11
// access:
size_type size()const;
size_type degree()const;
value_type& operator[](size_type i);
const value_type& operator[](size_type i)const;
std::vector<T> const& data() const;
std::vector<T>& data();
explicit operator bool() const;
// modify:
void set_zero();
// operators:
template <class U>
polynomial& operator +=(const U& value);
template <class U>
polynomial& operator -=(const U& value);
template <class U>
polynomial& operator *=(const U& value);
template <class U>
polynomial& operator /=(const U& value);
template <class U>
polynomial& operator %=(const U& value);
template <class U>
polynomial& operator +=(const polynomial<U>& value);
template <class U>
polynomial& operator -=(const polynomial<U>& value);
template <class U>
polynomial& operator *=(const polynomial<U>& value);
template <class U>
polynomial& operator /=(const polynomial<U>& value);
template <class U>
polynomial& operator %=(const polynomial<U>& value);
};
template <class T>
polynomial<T> operator + (const polynomial<T>& a, const polynomial<T>& b);
template <class T>
polynomial<T> operator - (const polynomial<T>& a, const polynomial<T>& b);
template <class T>
polynomial<T> operator * (const polynomial<T>& a, const polynomial<T>& b);
template <class T>
polynomial<T> operator / (const polynomial<T>& a, const polynomial<T>& b);
template <class T>
polynomial<T> operator % (const polynomial<T>& a, const polynomial<T>& b);
template <class T, class U>
polynomial<T> operator + (const polynomial<T>& a, const U& b);
template <class T, class U>
polynomial<T> operator - (const polynomial<T>& a, const U& b);
template <class T, class U>
polynomial<T> operator * (const polynomial<T>& a, const U& b);
template <class T, class U>
polynomial<T> operator / (const polynomial<T>& a, const U& b);
template <class T, class U>
polynomial<T> operator % (const polynomial<T>& a, const U& b);
template <class U, class T>
polynomial<T> operator + (const U& a, const polynomial<T>& b);
template <class U, class T>
polynomial<T> operator - (const U& a, const polynomial<T>& b);
template <class U, class T>
polynomial<T> operator * (const U& a, const polynomial<T>& b);
template <class T>
polynomial<T> operator - (const polynomial<T>& a);
template <class T>
polynomial<T> operator >>= (const U& a);
template <class T>
polynomial<T> operator >> (polynomial<T> const &a, const U& b);
template <class T>
polynomial<T> operator <<= (const U& a);
template <class T>
polynomial<T> operator << (polynomial<T> const &a, const U& b);
template <class T>
bool operator == (const polynomial<T> &a, const polynomial<T> &b);
template <class T>
bool operator != (const polynomial<T> &a, const polynomial<T> &b);
template <class T>
polynomial<T> pow(polynomial<T> base, int exp);
template <class charT, class traits, class T>
std::basic_ostream<charT, traits>& operator <<
(std::basic_ostream<charT, traits>& os, const polynomial<T>& poly);
template <typename T>
std::pair< polynomial<T>, polynomial<T> >
quotient_remainder(const polynomial<T>& a, const polynomial<T>& b);
} // namespace tools
}} // namespace boost { namespace math
[h4 Description]
This is a somewhat trivial class for polynomial manipulation.
See
* [@https://en.wikipedia.org/wiki/Polynomial Polynomial] and
* Donald E. Knuth, The Art of Computer Programming: Volume 2, Third edition, (1998)
Chapter 4.6.1, Algorithm D: Division of polynomials over a field.
Implementation is currently of the "naive" variety, with [bigo](N[super 2])
multiplication, for example. This class should not be used in
high-performance computing environments: it is intended for the
simple manipulation of small polynomials, typically generated
for special function approximation.
It does has division for polynomials over a [@https://en.wikipedia.org/wiki/Field_%28mathematics%29 field]
(here floating point, complex, etc)
and over a unique factorization domain (integers).
Division of polynomials over a field is compatible with
[@https://en.wikipedia.org/wiki/Euclidean_algorithm Euclidean GCD].
Advanced manipulations: the FFT, factorisation etc are
not currently provided. Submissions for these are of course welcome :-)
[h4:polynomial_examples Polynomial Arithmetic Examples]
[import ../../example/polynomial_arithmetic.cpp]
[polynomial_arithmetic_0]
[polynomial_arithmetic_1]
[polynomial_arithmetic_2]
for output:
[polynomial_output_1]
[polynomial_arithmetic_3]
for output
[polynomial_output_2]
[h5 Division, Quotient and Remainder]
[polynomial_arithmetic_4]
The source code is at [@../../example/polynomial_arithmetic.cpp polynomial_arithmetic.cpp]
[endsect] [/section:polynomials Polynomials]
[/
Copyright 2006 John Maddock and Paul A. Bristow.
Copyright 2015 Jeremy William Murphy.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
+176
View File
@@ -0,0 +1,176 @@
[section:rational Polynomial and Rational Function Evaluation]
[h4 Synopsis]
``
#include <boost/math/tools/rational.hpp>
``
// Polynomials:
template <std::size_t N, class T, class V>
V evaluate_polynomial(const T(&poly)[N], const V& val);
template <std::size_t N, class T, class V>
V evaluate_polynomial(const boost::array<T,N>& poly, const V& val);
template <class T, class U>
U evaluate_polynomial(const T* poly, U z, std::size_t count);
// Even polynomials:
template <std::size_t N, class T, class V>
V evaluate_even_polynomial(const T(&poly)[N], const V& z);
template <std::size_t N, class T, class V>
V evaluate_even_polynomial(const boost::array<T,N>& poly, const V& z);
template <class T, class U>
U evaluate_even_polynomial(const T* poly, U z, std::size_t count);
// Odd polynomials
template <std::size_t N, class T, class V>
V evaluate_odd_polynomial(const T(&a)[N], const V& z);
template <std::size_t N, class T, class V>
V evaluate_odd_polynomial(const boost::array<T,N>& a, const V& z);
template <class T, class U>
U evaluate_odd_polynomial(const T* poly, U z, std::size_t count);
// Rational Functions:
template <std::size_t N, class T, class V>
V evaluate_rational(const T(&a)[N], const T(&b)[N], const V& z);
template <std::size_t N, class T, class V>
V evaluate_rational(const boost::array<T,N>& a, const boost::array<T,N>& b, const V& z);
template <class T, class U, class V>
V evaluate_rational(const T* num, const U* denom, V z, unsigned count);
[h4 Description]
Each of the functions come in three variants: a pair of overloaded functions
where the order of the polynomial or rational function is evaluated at
compile time, and an overload that accepts a runtime variable for the size
of the coefficient array. Generally speaking, compile time evaluation of the
array size results in better type safety, is less prone to programmer errors,
and may result in better optimised code. The polynomial evaluation functions
in particular, are specialised for various array sizes, allowing for
loop unrolling, and one hopes, optimal inline expansion.
template <std::size_t N, class T, class V>
V evaluate_polynomial(const T(&poly)[N], const V& val);
template <std::size_t N, class T, class V>
V evaluate_polynomial(const boost::array<T,N>& poly, const V& val);
template <class T, class U>
U evaluate_polynomial(const T* poly, U z, std::size_t count);
Evaluates the [@http://en.wikipedia.org/wiki/Polynomial polynomial] described by
the coefficients stored in /poly/.
If the size of the array is specified at runtime, then the polynomial
most have order /count-1/ with /count/ coefficients. Otherwise it has
order /N-1/ with /N/ coefficients.
Coefficients should be stored such that the coefficients for the x[super i] terms
are in poly[i].
The types of the coefficients and of variable
/z/ may differ as long as /*poly/ is convertible to type /U/.
This allows, for example, for the coefficient table
to be a table of integers if this is appropriate.
template <std::size_t N, class T, class V>
V evaluate_even_polynomial(const T(&poly)[N], const V& z);
template <std::size_t N, class T, class V>
V evaluate_even_polynomial(const boost::array<T,N>& poly, const V& z);
template <class T, class U>
U evaluate_even_polynomial(const T* poly, U z, std::size_t count);
As above, but evaluates an even polynomial: one where all the powers
of /z/ are even numbers. Equivalent to calling
`evaluate_polynomial(poly, z*z, count)`.
template <std::size_t N, class T, class V>
V evaluate_odd_polynomial(const T(&a)[N], const V& z);
template <std::size_t N, class T, class V>
V evaluate_odd_polynomial(const boost::array<T,N>& a, const V& z);
template <class T, class U>
U evaluate_odd_polynomial(const T* poly, U z, std::size_t count);
As above but evaluates a polynomial where all the powers are odd numbers.
Equivalent to `evaluate_polynomial(poly+1, z*z, count-1) * z + poly[0]`.
template <std::size_t N, class T, class U, class V>
V evaluate_rational(const T(&num)[N], const U(&denom)[N], const V& z);
template <std::size_t N, class T, class U, class V>
V evaluate_rational(const boost::array<T,N>& num, const boost::array<U,N>& denom, const V& z);
template <class T, class U, class V>
V evaluate_rational(const T* num, const U* denom, V z, unsigned count);
Evaluates the rational function (the ratio of two polynomials) described by
the coefficients stored in /num/ and /demom/.
If the size of the array is specified at runtime then both
polynomials most have order /count-1/ with /count/ coefficients.
Otherwise both polynomials have order /N-1/ with /N/ coefficients.
Array /num/ describes the numerator, and /demon/ the denominator.
Coefficients should be stored such that the coefficients for the x[super i ] terms
are in num[i] and denom[i].
The types of the coefficients and of variable
/v/ may differ as long as /*num/ and /*denom/ are convertible to type /V/.
This allows, for example, for one or both of the coefficient tables
to be a table of integers if this is appropriate.
These functions are designed to safely evaluate the result, even when the value
/z/ is very large. As such they do not take advantage of compile time array
sizes to make any optimisations. These functions are best reserved for situations
where /z/ may be large: if you can be sure that numerical overflow will not occur
then polynomial evaluation with compile-time array sizes may offer slightly
better performance.
[h4 Implementation]
Polynomials are evaluated by
[@http://en.wikipedia.org/wiki/Horner_algorithm Horners method].
If the array size is known at
compile time then the functions dispatch to size-specific implementations
that unroll the evaluation loop.
Rational evaluation is by
[@http://en.wikipedia.org/wiki/Horner_algorithm Horners method]:
with the two polynomials being evaluated
in parallel to make the most of the processors floating-point pipeline.
If /v/ is greater than one, then the polynomials are evaluated in reverse
order as polynomials in ['1\/v]: this avoids unnecessary numerical overflow when the
coefficients are large.
Both the polynomial and rational function evaluation algorithms can be
tuned using various configuration macros to provide optimal performance
for a particular combination of compiler and platform. This includes
support for second-order Horner's methods. The various options are
[link math_toolkit.tuning documented here]. However, the performance
benefits to be gained from these are marginal on most current hardware,
consequently it's best to run the
[link math_toolkit.perf_test_app performance test application] before
changing the default settings.
[endsect] [/section:rational Polynomial and Rational Function Evaluation]
[/
Copyright 2006 John Maddock and Paul A. Bristow.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
+139
View File
@@ -0,0 +1,139 @@
[section:error_test Relative Error and Testing]
[h4 Synopsis]
``
#include <boost/math/tools/test.hpp>
``
[important
The header `boost/math/tools/test.hpp` is located under `libs/math/include_private`
and is not installed to the usual locations by default, you will need to add `libs/math/include_private`
to your compiler's include path in order to use this header.]
template <class T>
T relative_error(T a, T b);
template <class A, class F1, class F2>
test_result<see-below> test(const A& a, F1 test_func, F2 expect_func);
[h4 Description]
template <class T>
T relative_error(T a, T v);
Returns the relative error between /a/ and /v/ using the usual formula:
[equation error1]
In addition the value returned is zero if:
* Both /a/ and /v/ are infinite.
* Both /a/ and /v/ are denormalised numbers or zero.
Otherwise if only one of /a/ and /v/ is zero then the value returned is 1.
template <class A, class F1, class F2>
test_result<see-below> test(const A& a, F1 test_func, F2 expect_func);
This function is used for testing a function against tabulated test data.
The return type contains statistical data on the relative errors (max, mean,
variance, and the number of test cases etc), as well as the row of test data that
caused the largest relative error. Public members of type test_result are:
[variablelist
[[`unsigned worst()const;`][Returns the row at which the worst error occurred.]]
[[`T min()const;`][Returns the smallest relative error found.]]
[[`T max()const;`][Returns the largest relative error found.]]
[[`T mean()const;`][Returns the mean error found.]]
[[`boost::uintmax_t count()const;`][Returns the number of test cases.]]
[[`T variance()const;`][Returns the variance of the errors found.]]
[[`T variance1()const;`][Returns the unbiased variance of the errors found.]]
[[`T rms()const`][Returns the Root Mean Square, or quadratic mean of the errors.]]
[[`test_result& operator+=(const test_result& t)`][Combines two test_result's into
a single result.]]
]
The template parameter of test_result, is the same type as the values in the two
dimensional array passed to function /test/, roughly that's
`A::value_type::value_type`.
Parameter /a/ is a matrix of test data: and must be a standard library Sequence type,
that contains another Sequence type:
typically it will be a two dimensional instance of
[^boost::array]. Each row
of /a/ should contain all the parameters that are passed to the function
under test as well as the expected result.
Parameter /test_func/ is the function under test, it is invoked with each row
of test data in /a/. Typically type F1 is created with Boost.Lambda: see
the example below.
Parameter /expect_func/ is a functor that extracts the expected result
from a row of test data in /a/. Typically type F2 is created with Boost.Lambda: see
the example below.
If the function under test returns a non-finite value when a finite result is
expected, or if a gross error is found, then a message is sent to `std::cerr`,
and a call to BOOST_ERROR() made (which means that including this header requires
you use Boost.Test). This is mainly a debugging/development aid
(and a good place for a breakpoint).
[h4 Example]
Suppose we want to test the tgamma and lgamma functions, we can create a
two dimensional matrix of test data, each row is one test case, and contains
three elements: the input value, and the expected results for the tgamma and
lgamma functions respectively.
static const boost::array<boost::array<TestType, 3>, NumberOfTests>
factorials = {
/* big array of test data goes here */
};
Now we can invoke the test function to test tgamma:
using namespace boost::math::tools;
using namespace boost::lambda;
// get a pointer to the function under test:
TestType (*funcp)(TestType) = boost::math::tgamma;
// declare something to hold the result:
test_result<TestType> result;
//
// and test tgamma against data:
//
result = test(
factorials,
bind(funcp, ret<TestType>(_1[0])), // calls tgamma with factorials[row][0]
ret<TestType>(_1[1]) // extracts the expected result from factorials[row][1]
);
//
// Print out some results:
//
std::cout << "The Mean was " << result.mean() << std::endl;
std::cout << "The worst error was " << (result.max)() << std::endl;
std::cout << "The worst error was at row " << result.worst_case() << std::endl;
//
// same again with lgamma this time:
//
funcp = boost::math::lgamma;
result = test(
factorials,
bind(funcp, ret<TestType>(_1[0])), // calls tgamma with factorials[row][0]
ret<TestType>(_1[2]) // extracts the expected result from factorials[row][2]
);
//
// etc ...
//
[endsect][/section:error_test Relative Error and Testing]
[/
Copyright 2006 John Maddock and Paul A. Bristow.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
+141
View File
@@ -0,0 +1,141 @@
[section:series_evaluation Series Evaluation]
[h4 Synopsis]
``
#include <boost/math/tools/series.hpp>
``
namespace boost{ namespace math{ namespace tools{
template <class Functor, class U, class V>
inline typename Functor::result_type sum_series(Functor& func, const U& tolerance, boost::uintmax_t& max_terms, const V& init_value);
template <class Functor, class U, class V>
inline typename Functor::result_type sum_series(Functor& func, const U& tolerance, boost::uintmax_t& max_terms);
//
// The following interfaces are now deprecated:
//
template <class Functor>
typename Functor::result_type sum_series(Functor& func, int bits);
template <class Functor>
typename Functor::result_type sum_series(Functor& func, int bits, boost::uintmax_t& max_terms);
template <class Functor, class U>
typename Functor::result_type sum_series(Functor& func, int bits, U init_value);
template <class Functor, class U>
typename Functor::result_type sum_series(Functor& func, int bits, boost::uintmax_t& max_terms, U init_value);
template <class Functor>
typename Functor::result_type kahan_sum_series(Functor& func, int bits);
template <class Functor>
typename Functor::result_type kahan_sum_series(Functor& func, int bits, boost::uintmax_t& max_terms);
}}} // namespaces
[h4 Description]
These algorithms are intended for the
[@http://en.wikipedia.org/wiki/Series_%28mathematics%29 summation of infinite series].
Each of the algorithms takes a nullary-function object as the first argument:
the function object will be repeatedly invoked to pull successive terms from
the series being summed.
The second argument is the precision required,
summation will stop when the next term is less than
/tolerance/ times the result. The deprecated versions of sum_series
take an integer number of bits here - internally they just convert this to
a tolerance and forward the call.
The third argument /max_terms/ sets an upper limit on the number
of terms of the series to evaluate. In addition, on exit the function will
set /max_terms/ to the actual number of terms of the series that were
evaluated: this is particularly useful for profiling the convergence
properties of a new series.
The final optional argument /init_value/ is the initial value of the sum
to which the terms of the series should be added. This is useful in two situations:
* Where the first value of the series has a different formula to successive
terms. In this case the first value in the series can be passed as the
last argument and the logic of the function object can then be simplified
to return subsequent terms.
* Where the series is being added (or subtracted) from some other value:
termination of the series will likely occur much more rapidly if that other
value is passed as the last argument. For example, there are several functions
that can be expressed as /1 - S(z)/ where S(z) is an infinite series. In this
case, pass -1 as the last argument and then negate the result of the summation
to get the result of /1 - S(z)/.
The two /kahan_sum_series/ variants of these algorithms maintain a carry term
that corrects for roundoff error during summation.
They are inspired by the
[@http://en.wikipedia.org/wiki/Kahan_Summation_Algorithm /Kahan Summation Formula/]
that appears in
[@http://docs.sun.com/source/806-3568/ncg_goldberg.html What Every Computer Scientist Should Know About Floating-Point Arithmetic].
However, it should be pointed out that there are very few series that require
summation in this way.
[h4 Example]
Let's suppose we want to implement /log(1+x)/ via its infinite series,
[equation log1pseries]
We begin by writing a small function object to return successive terms
of the series:
template <class T>
struct log1p_series
{
// we must define a result_type typedef:
typedef T result_type;
log1p_series(T x)
: k(0), m_mult(-x), m_prod(-1){}
T operator()()
{
// This is the function operator invoked by the summation
// algorithm, the first call to this operator should return
// the first term of the series, the second call the second
// term and so on.
m_prod *= m_mult;
return m_prod / ++k;
}
private:
int k;
const T m_mult;
T m_prod;
};
Implementing log(1+x) is now fairly trivial:
template <class T>
T log1p(T x)
{
// We really should add some error checking on x here!
assert(std::fabs(x) < 1);
// Construct the series functor:
log1p_series<T> s(x);
// Set a limit on how many iterations we permit:
boost::uintmax_t max_iter = 1000;
// Add it up, with enough precision for full machine precision:
return tools::sum_series(s, std::numeric_limits<T>::epsilon(), max_iter);
}
[endsect][/section Series Evaluation]
[/
Copyright 2006 John Maddock and Paul A. Bristow.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
+446
View File
@@ -0,0 +1,446 @@
[section:test_data Graphing, Profiling, and Generating Test Data for Special Functions]
The class `test_data` and associated helper functions are designed so that in just
a few lines of code you should be able to:
* Profile a continued fraction, or infinite series for convergence and accuracy.
* Generate csv data from a special function that can be imported into your favorite
graphing program (or spreadsheet) for further analysis.
* Generate high precision test data.
[h4 Synopsis]
#include <boost/math/tools/test_data.hpp>
[important
This is a non-core Boost.Math header that is predominantly used for internal
maintenance of the library: as a result the library is located under
`libs/math/include_private` and you will need to add that directory to
your include path in order to use this feature.
]
namespace boost{ namespace math{ namespace tools{
enum parameter_type
{
random_in_range = 0,
periodic_in_range = 1,
power_series = 2,
dummy_param = 0x80,
};
template <class T>
struct parameter_info;
template <class T>
parameter_info<T> make_random_param(T start_range, T end_range, int n_points);
template <class T>
parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points);
template <class T>
parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent);
template <class T>
bool get_user_parameter_info(parameter_info<T>& info, const char* param_name);
template <class T>
class test_data
{
public:
typedef std::vector<T> row_type;
typedef row_type value_type;
private:
typedef std::set<row_type> container_type;
public:
typedef typename container_type::reference reference;
typedef typename container_type::const_reference const_reference;
typedef typename container_type::iterator iterator;
typedef typename container_type::const_iterator const_iterator;
typedef typename container_type::difference_type difference_type;
typedef typename container_type::size_type size_type;
// creation:
test_data(){}
template <class F>
test_data(F func, const parameter_info<T>& arg1);
// insertion:
template <class F>
test_data& insert(F func, const parameter_info<T>& arg1);
template <class F>
test_data& insert(F func, const parameter_info<T>& arg1,
const parameter_info<T>& arg2);
template <class F>
test_data& insert(F func, const parameter_info<T>& arg1,
const parameter_info<T>& arg2,
const parameter_info<T>& arg3);
void clear();
// access:
iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
bool operator==(const test_data& d)const;
bool operator!=(const test_data& d)const;
void swap(test_data& other);
size_type size()const;
size_type max_size()const;
bool empty()const;
bool operator < (const test_data& dat)const;
bool operator <= (const test_data& dat)const;
bool operator > (const test_data& dat)const;
bool operator >= (const test_data& dat)const;
};
template <class charT, class traits, class T>
std::basic_ostream<charT, traits>& write_csv(
std::basic_ostream<charT, traits>& os,
const test_data<T>& data);
template <class charT, class traits, class T>
std::basic_ostream<charT, traits>& write_csv(
std::basic_ostream<charT, traits>& os,
const test_data<T>& data,
const charT* separator);
template <class T>
std::ostream& write_code(std::ostream& os,
const test_data<T>& data,
const char* name);
}}} // namespaces
[h4 Description]
This tool is best illustrated with the following series of examples.
The functionality of test_data is split into the following parts:
* A functor that implements the function for which data is being generated:
this is the bit you have to write.
* One of more parameters that are to be passed to the functor, these are
described in fairly abstract terms: give me N points distributed like /this/ etc.
* The class test_data, that takes the functor and descriptions of the parameters
and computes how ever many output points have been requested, these are stored
in a sorted container.
* Routines to iterate over the test_data container and output the data in either
csv format, or as C++ source code (as a table using Boost.Array).
[h5 Example 1: Output Data for Graph Plotting]
For example, lets say we want to graph the lgamma function between -3 and 100,
one could do this like so:
#include <boost/math/tools/test_data.hpp>
#include <boost/math/special_functions/gamma.hpp>
int main()
{
using namespace boost::math::tools;
// create an object to hold the data:
test_data<double> data;
// insert 500 points at uniform intervals between just after -3 and 100:
double (*pf)(double) = boost::math::lgamma;
data.insert(pf, make_periodic_param(-3.0 + 0.00001, 100.0, 500));
// print out in csv format:
write_csv(std::cout, data, ", ");
return 0;
}
Which, when plotted, results in:
[graph lgamma]
[h5 Example 2: Creating Test Data]
As a second example, let's suppose we want to create highly accurate test
data for a special function. Since many special functions have two or
more independent parameters, it's very hard to effectively cover all of
the possible parameter space without generating gigabytes of data at
great computational expense. A second best approach is to provide the tools
by which a user (or the library maintainer) can quickly generate more data
on demand to probe the function over a particular domain of interest.
In this example we'll generate test data for the beta function using
[@http://shoup.net/ntl/doc/RR.txt NTL::RR] at 1000 bit precision.
Rather than call our generic
version of the beta function, we'll implement a deliberately naive version
of the beta function using lgamma, and rely on the high precision of the
data type used to get results accurate to at least 128-bit precision. In this
way our test data is independent of whatever clever tricks we may wish to
use inside the our beta function.
To start with then, here's the function object that creates the test data:
#include <boost/math/tools/ntl.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/tools/test_data.hpp>
#include <fstream>
#include <boost/math/tools/test_data.hpp>
using namespace boost::math::tools;
struct beta_data_generator
{
NTL::RR operator()(NTL::RR a, NTL::RR b)
{
//
// If we throw a domain error then test_data will
// ignore this input point. We'll use this to filter
// out all cases where a < b since the beta function
// is symmetrical in a and b:
//
if(a < b)
throw std::domain_error("");
// very naively calculate spots with lgamma:
NTL::RR g1, g2, g3;
int s1, s2, s3;
g1 = boost::math::lgamma(a, &s1);
g2 = boost::math::lgamma(b, &s2);
g3 = boost::math::lgamma(a+b, &s3);
g1 += g2 - g3;
g1 = exp(g1);
g1 *= s1 * s2 * s3;
return g1;
}
};
To create the data, we'll need to input the domains for a and b
for which the function will be tested: the function `get_user_parameter_info`
is designed for just that purpose. The start of main will look something like:
// Set the precision on RR:
NTL::RR::SetPrecision(1000); // bits.
NTL::RR::SetOutputPrecision(40); // decimal digits.
parameter_info<NTL::RR> arg1, arg2;
test_data<NTL::RR> data;
std::cout << "Welcome.\n"
"This program will generate spot tests for the beta function:\n"
" beta(a, b)\n\n";
bool cont;
std::string line;
do{
// prompt the user for the domain of a and b to test:
get_user_parameter_info(arg1, "a");
get_user_parameter_info(arg2, "b");
// create the data:
data.insert(beta_data_generator(), arg1, arg2);
// see if the user want's any more domains tested:
std::cout << "Any more data [y/n]?";
std::getline(std::cin, line);
boost::algorithm::trim(line);
cont = (line == "y");
}while(cont);
[caution At this point one potential stumbling block should be mentioned:
test_data<>::insert will create a matrix of test data when there are two
or more parameters, so if we have two parameters and we're asked for
a thousand points on each, that's a ['million test points in total].
Don't say you weren't warned!]
There's just one final step now, and that's to write the test data to file:
std::cout << "Enter name of test data file [default=beta_data.ipp]";
std::getline(std::cin, line);
boost::algorithm::trim(line);
if(line == "")
line = "beta_data.ipp";
std::ofstream ofs(line.c_str());
write_code(ofs, data, "beta_data");
The format of the test data looks something like:
#define SC_(x) static_cast<T>(BOOST_JOIN(x, L))
static const boost::array<boost::array<T, 3>, 1830>
beta_med_data = {
SC_(0.4883005917072296142578125),
SC_(0.4883005917072296142578125),
SC_(3.245912809500479157065104747353807392371),
SC_(3.5808107852935791015625),
SC_(0.4883005917072296142578125),
SC_(1.007653173802923954909901438393379243537),
/* ... lots of rows skipped */
};
The first two values in each row are the input parameters that were passed
to our functor and the last value is the return value from the functor.
Had our functor returned a __tuple rather than a value, then we would have had
one entry for each element in the tuple in addition to the input parameters.
The first #define serves two purposes:
* It reduces the file sizes considerably: all those `static_cast`'s add up to a lot
of bytes otherwise (they are needed to suppress compiler warnings when `T` is
narrower than a `long double`).
* It provides a useful customisation point: for example if we were testing
a user-defined type that has more precision than a `long double` we could change
it to:
[^#define SC_(x) lexical_cast<T>(BOOST_STRINGIZE(x))]
in order to ensure that no truncation of the values occurs prior to conversion
to `T`. Note that this isn't used by default as it's rather hard on the compiler
when the table is large.
[h5 Example 3: Profiling a Continued Fraction for Convergence and Accuracy]
Alternatively, lets say we want to profile a continued fraction for
convergence and error. As an example, we'll use the continued fraction
for the upper incomplete gamma function, the following function object
returns the next a[sub N ] and b[sub N ] of the continued fraction
each time it's invoked:
template <class T>
struct upper_incomplete_gamma_fract
{
private:
T z, a;
int k;
public:
typedef std::pair<T,T> result_type;
upper_incomplete_gamma_fract(T a1, T z1)
: z(z1-a1+1), a(a1), k(0)
{
}
result_type operator()()
{
++k;
z += 2;
return result_type(k * (a - k), z);
}
};
We want to measure both the relative error, and the rate of convergence
of this fraction, so we'll write a functor that returns both as a __tuple:
class test_data will unpack the tuple for us, and create one column of data
for each element in the tuple (in addition to the input parameters):
#include <boost/math/tools/test_data.hpp>
#include <boost/math/tools/test.hpp>
#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/tools/ntl.hpp>
#include <boost/math/tools/tuple.hpp>
template <class T>
struct profile_gamma_fraction
{
typedef ``__tuple``<T, T> result_type;
result_type operator()(T val)
{
using namespace boost::math::tools;
// estimate the true value, using arbitary precision
// arithmetic and NTL::RR:
NTL::RR rval(val);
upper_incomplete_gamma_fract<NTL::RR> f1(rval, rval);
NTL::RR true_val = continued_fraction_a(f1, 1000);
//
// Now get the aproximation at double precision, along with the number of
// iterations required:
boost::uintmax_t iters = std::numeric_limits<boost::uintmax_t>::max();
upper_incomplete_gamma_fract<T> f2(val, val);
T found_val = continued_fraction_a(f2, std::numeric_limits<T>::digits, iters);
//
// Work out the relative error, as measured in units of epsilon:
T err = real_cast<T>(relative_error(true_val, NTL::RR(found_val)) / std::numeric_limits<T>::epsilon());
//
// now just return the results as a tuple:
return boost::math::make_tuple(err, iters);
}
};
Feeding that functor into test_data allows rapid output of csv data,
for whatever type `T` we may be interested in:
int main()
{
using namespace boost::math::tools;
// create an object to hold the data:
test_data<double> data;
// insert 500 points at uniform intervals between just after 0 and 100:
data.insert(profile_gamma_fraction<double>(), make_periodic_param(0.01, 100.0, 100));
// print out in csv format:
write_csv(std::cout, data, ", ");
return 0;
}
This time there's no need to plot a graph, the first few rows are:
a and z, Error/epsilon, Iterations required
0.01, 9723.14, 4726
1.0099, 9.54818, 87
2.0098, 3.84777, 40
3.0097, 0.728358, 25
4.0096, 2.39712, 21
5.0095, 0.233263, 16
So it's pretty clear that this fraction shouldn't be used for small values
of a and z.
[h4 reference]
Most of this tool has been described already in the examples above, we'll
just add the following notes on the non-member functions:
template <class T>
parameter_info<T> make_random_param(T start_range, T end_range, int n_points);
Tells class test_data to test /n_points/ random values in the range
[start_range,end_range].
template <class T>
parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points);
Tells class test_data to test /n_points/ evenly spaced values in the range
[start_range,end_range].
template <class T>
parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent);
Tells class test_data to test points of the form ['basis + R * 2[super expon]] for each
/expon/ in the range [start_exponent, end_exponent], and /R/ a random number in \[0.5, 1\].
template <class T>
bool get_user_parameter_info(parameter_info<T>& info, const char* param_name);
Prompts the user for the parameter range and form to use.
Finally, if we don't want the parameter to be included in the output, we can tell
test_data by setting it a "dummy parameter":
parameter_info<double> p = make_random_param(2.0, 5.0, 10);
p.type |= dummy_param;
This is useful when the functor used transforms the parameter in some way
before passing it to the function under test, usually the functor will then
return both the transformed input and the result in a tuple, so there's no
need for the original pseudo-parameter to be included in program output.
[endsect][/section:test_data Graphing, Profiling, and Generating Test Data for Special Functions]
[/
Copyright 2006 John Maddock and Paul A. Bristow.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
+31
View File
@@ -0,0 +1,31 @@
[section:tuples Tuples]
[h4 Synopsis]
``
#include <boost/math/tools/tuple.hpp>
``
[h4 Description]
This header defines the type `boost::math::tuple`,
the associated free functions `ignore`, `tie`, `make_tuple`, `get`,
and associated types `tuple_size` and `tuple_element`.
These types and functions are aliases for:
* `std::tuple` etc when available, otherwise:
* `std::tr1::tuple` etc when available, otherwise:
* `boost::fusion::tuple` etc if the compiler supports it, otherwise:
* `boost::tuple`.
So this `boost::math::tuple` is strongly recommended for maximum portability.
[endsect] [/section:Tuples Tuples]
[/
Copyright 2010 John Maddock.
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]