[DEV] add v1.76.0

This commit is contained in:
2021-10-05 21:37:46 +02:00
parent a97e9ae7d4
commit d0115b733d
45133 changed files with 4744437 additions and 1026325 deletions

View File

@@ -0,0 +1,77 @@
[/
Copyright Nick Thompson, John Maddock, 2020
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).
]
[section:agm Arithmetic-Geometric Mean]
[h4 Synopsis]
#include <boost/math/tools/agm.hpp>
namespace boost::math::tools {
template<typename Real>
Real agm(Real a0, Real g0);
} // namespaces
The function `agm` produces the limiting value of the sequence
[$../equations/agm_sequence.svg]
A basic usage is
double G = boost::math::tools::agm(sqrt(2.0), 1.0);
The AGM inequality
[$../equations/agm_sequence.svg]
shows that
[$../equations/agm_bound.svg]
We use this condition internally to measure convergence; however, there is no need to worry about putting arguments in the correct order since we extend `agm` to a symmetric function by definition.
Both arguments must be non-negative, as the sequence becomes complex for negative arguments.
(We have not implemented the complex-valued AGM sequence.)
The function `agm` is "essentially" one-dimensional, as the scale invariance `agm(k*x, k*y) == k*agm(x,y)` always allows us to take one argument to be unity.
The following ULP plot has been generated with the function `agm(x, Real(1))`:
[$../graphs/agm_ulps_plot.svg]
The graph above shows an ulps plot of the Boost implementation of `agm(x, Real(1))`.
An ~2 ULP bound is to be expected.
A google benchmark for various types is available in `boost/libs/math/reporting/performance/test_agm.cpp`; some results on a consumer laptop are provided for convenience:
```
Run on (16 X 2300 MHz CPU s)
CPU Caches:
L1 Data 32K (x8)
L1 Instruction 32K (x8)
L2 Unified 262K (x8)
L3 Unified 16777K (x1)
Load Average: 2.02, 2.14, 2.00
-------------------------------------------------------------------------------
Benchmark Time CPU Iterations
-------------------------------------------------------------------------------
AGM<float> 8.52 ns 8.51 ns 59654685
AGM<double> 13.5 ns 13.5 ns 51709746
AGM<long double> 30.6 ns 30.6 ns 18745247
AGM<boost::multiprecision::float128> 2332 ns 2332 ns 299303
```
If any inputs are NaNs, the result is a NaN.
If any inputs are +∞, the result is +∞, unless the other argument fails NaN or negative validation.
[heading References]
* Steven R. Finch. ['Mathematical Constants] Cambridge, 2003.
[endsect]

View File

@@ -0,0 +1,55 @@
[/
Copyright Nick Thompson, 2020
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).
]
[section:centered_continued_fraction Centered Continued Fractions]
#include <boost/math/tools/centered_continued_fraction.hpp>
namespace boost::math::tools {
template<typename Real, typename Z = int64_t>
class centered_continued_fraction {
public:
centered_continued_fraction(Real x);
Real khinchin_geometric_mean() const;
template<typename T, typename Z_>
friend std::ostream& operator<<(std::ostream& out, centered_continued_fraction<T, Z_>& ccf);
};
}
The `centered_continued_fraction` class provided by Boost affords the ability to convert a floating point number into a centered continued fraction.
Unlike the simple continued fraction, a centered continued fraction allows partial denominators to be negative.
Here's a minimal working example:
using boost::math::constants::pi;
using boost::math::tools::centered_continued_fraction;
auto cfrac = centered_continued_fraction(pi<long double>());
std::cout << "π ≈ " << cfrac << "\n";
// Prints:
// π ≈ [3; 7, 16, -294, 3, -4, 5, -15, -3, 2, 2]
This function achieves the same end as the Maple syntax
[$../images/maple_cfrac.png]
You should convince yourself that the Maple output and the notation we use are in fact the same thing.
The class computes partial denominators which simultaneously computing convergents with the modified Lentz's algorithm.
Once a convergent is within a few ulps of the input value, the computation stops.
Just like simple continued fractions, centered continued fractions admit a "Khinchin constant".
The value of this constant is ~5.454517 (see [@http://jeremiebourdon.free.fr/data/Khintchine.pdf here]).
We can evaluate the "empirical Khinchin constant" for a particular number via
cfrac.khinchin_geometric_mean();
If this is close to 5.454517, then the number is probably uninteresting with respect to its characterization as a centered continued fraction.
[endsect]

View File

@@ -0,0 +1,106 @@
[/
Copyright Nick Thompson 2020
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).
]
[section:cohen_acceleration Cohen Acceleration]
[h4 Synopsis]
#include <boost/math/tools/cohen_acceleration.hpp>
namespace boost::math::tools {
template<class G>
auto cohen_acceleration(G& generator, int64_t n = -1);
} // namespaces
The function `cohen_acceleration` rapidly computes the limiting value of an alternating series via a technique developed by [@https://www.johndcook.com/blog/2020/08/06/cohen-acceleration/ Henri Cohen et al].
To compute
[$../equations/alternating_series.svg]
we first define a callable that produces /a/[sub /k/] on the kth call.
For example, suppose we wish to compute
[$../equations/zeta_related_alternating.svg]
First, we need to define a callable which returns the requisite terms:
template<typename Real>
class G {
public:
G(){
k_ = 0;
}
Real operator()() {
k_ += 1;
return 1/(k_*k_);
}
private:
Real k_;
};
Then we pass this into the `cohen_acceleration` function:
auto gen = G<double>();
double computed = cohen_acceleration(gen);
See `cohen_acceleration.cpp` in the `examples` directory for more.
The number of terms consumed is computed from the error model
[$../equations/cohen_acceleration_error_model.svg]
and must be computed /a priori/.
If we read the reference carefully, we notice that this error model is derived under the assumption that the terms /a/[sub /k/] are given as the moments of a positive measure on [0,1].
If this assumption does not hold, then the number of terms chosen by the method is incorrect.
Hence we permit the user to provide a second argument to specify the number of terms:
double computed = cohen_acceleration(gen, 5);
/Nota bene/: When experimenting with this option, we found that adding more terms was no guarantee of additional accuracy, and could not find an example where a user-provided number of terms outperformed the default.
In addition, it is easy to generate intermediates which overflow if we let /n/ grow too large.
Hence we recommend only playing with this parameter to /decrease/ the default number of terms to increase speed.
[heading Performance]
To see that Cohen acceleration is in fact faster than naive summation for the same level of relative accuracy, we can run the `reporting/performance/cohen_acceleration_performance.cpp` file.
This benchmark computes the alternating Basel series discussed above:
```
Running ./reporting/performance/cohen_acceleration_performance.x
Run on (16 X 2300 MHz CPU s)
CPU Caches:
L1 Data 32 KiB (x8)
L1 Instruction 32 KiB (x8)
L2 Unified 256 KiB (x8)
L3 Unified 16384 KiB (x1)
Load Average: 4.13, 3.71, 3.30
-----------------------------------------------------------------
Benchmark Time
-----------------------------------------------------------------
CohenAcceleration<float> 20.7 ns
CohenAcceleration<double> 64.6 ns
CohenAcceleration<long double> 115 ns
NaiveSum<float> 4994 ns
NaiveSum<double> 112803698 ns
NaiveSum<long double> 5009564877 ns
```
In fact not only does the naive sum take orders of magnitude longer to compute, it is less accurate as well.
[heading References]
* Cohen, Henri, Fernando Rodriguez Villegas, and Don Zagier. ['Convergence acceleration of alternating series.] Experimental mathematics 9.1 (2000): 3-12.
[endsect]

View File

@@ -0,0 +1,44 @@
[/
Copyright Nick Thompson, 2020
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).
]
[section:engel_expansion Engel Expansion]
#include <boost/math/tools/engel_expansion.hpp>
namespace boost::math::tools {
template<typename Real, typename Z = int64_t>
class engel_expansion {
public:
engel_expansion(Real x);
std::vector<Z> const & digits() const;
template<typename T, typename Z_>
friend std::ostream& operator<<(std::ostream& out, engel_expansion<T, Z>& engel);
};
}
The `engel_expansion` class provided by Boost converts a floating point number into an [@https://en.wikipedia.org/wiki/Engel_expansion Engel series].
Here's a minimal working example:
using boost::math::constants::e;
using boost::math::tools::engel_expansion;
auto engel = engel_expansion(e<double>());
std::cout << "e ≈ " << engel << "\n";
// Prints:
// e ≈ {1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
The digits of an Engel expansion tend to grow exponentially, so the integer template option might need to be utilized.
For example, we can use a wider integer type via
using boost::multiprecision::checked_int1024_t;
auto engel = engel_expansion<__float128, checked_int1024_t>(pi<__float128>());
[endsect]

View File

@@ -69,7 +69,7 @@ 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.]]
This can be either an arithmetic or complex type, or a std::pair<> of arithmetic or complex types.]]
[[g()] [Returns an object of type Gen::result_type.
Each time this operator is called then the next pair of /a/ and /b/
@@ -100,6 +100,10 @@ Lentz, W.J. 1976, Applied Optics, vol. 15, pp. 668-671.
[h4 Examples]
[import ../../example/continued_fractions.cpp]
All of these examples are in [@../../example/continued_fractions.cpp continued_fractions.cpp].
The [@http://en.wikipedia.org/wiki/Golden_ratio golden ratio phi = 1.618033989...]
can be computed from the simplest continued fraction of all:
@@ -107,22 +111,11 @@ can be computed from the simplest continued fraction of all:
We begin by defining a generator function:
template <class T>
struct golden_ratio_fraction
{
typedef T result_type;
result_type operator()
{
return 1;
}
};
[golden_ratio_1]
The golden ratio can then be computed to double precision using:
continued_fraction_a(
golden_ratio_fraction<double>(),
std::numeric_limits<double>::epsilon());
[cf_gr]
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
@@ -132,41 +125,48 @@ the tan function is defined by:
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);
}
};
[cf_tan_fraction]
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());
}
[cf_tan]
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]
Now we'll look at a couple of complex number examples, starting with the exponential
integral which can be calculated via:
[equation expint_n_3]
So our functor looks like this:
[cf_expint_fraction]
We can finish the example by wrapping everything up in a function:
[cf_expint]
Notice how the termination condition is still expressed as a complex number, albeit one with zero imaginary part.
Our final example will use [^continued_fraction_a], in fact there is only one special function in our code
which uses that variant, and it's the upper incomplete gamma function (Q), which can be calculated via:
[equation igamma9]
In this case the first couple of terms are different from the rest, so our fraction will start with the first
"regular" a term:
[cf_upper_gamma_fraction]
So now we can implement Q, this time using [^continued_fraction_a]:
[cf_gamma_Q]
[endsect] [/section:cf Continued Fraction Evaluation]
[/
Copyright 2006 John Maddock and Paul A. Bristow.

View File

@@ -0,0 +1,62 @@
[/
Copyright Nick Thompson, 2020
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).
]
[section:luroth_expansion Luroth Expansions]
#include <boost/math/tools/luroth_expansion.hpp>
namespace boost::math::tools {
template<typename Real, typename Z = int64_t>
class luroth_expansion {
public:
luroth_expansion(Real x);
std::vector<Z> const & digits() const;
Real digit_geometric_mean() const;
template<typename T, typename Z_>
friend std::ostream& operator<<(std::ostream& out, luroth_expansion<T, Z_>& luroth);
};
}
The `luroth_expansion` class provided by Boost expands a floating point number into a Lüroth representation, i.e.,
[$../equations/luroth_expansion.svg]
The numbers /d/[sub i] are called digits or denominators; we use the terminology digits, since technically in our notation /d/[sub 0] is not a denominator.
Here's a minimal working example:
using boost::math::constants::pi;
using boost::math::tools::luroth_expansion;
auto luroth = luroth_expansion(pi<long double>());
std::cout << "π ≈ " << luroth << "\n";
// Prints:
// π ≈ ((3; 7, 1, 1, 1, 2, 1, 4, 23, 4, 1, 1, 1, 1, 80, 1, 1, 5))
The class computes denominators while simultaneously computing convergents.
Once a convergent is within a few ulps of the input value, the computation stops.
/Nota bene:/ There is an alternative definition of the Lüroth representation where every digit is shifted by 1.
We follow the definition given in Kalpazidou; with the modification that we do not constrain the input to be in the interval [0,1]
and let the first digit be the floor of the input.
For almost all real numbers, the geometric mean of the digits converges to a constant which is approximately 2.2001610580.
This is "Khinchin's constant" for the Lüroth representation.
[heading References]
* Kalpazidou, Sofia. "Khintchine's constant for Lüroth representation." Journal of Number Theory 29.2 (1988): 196-205.
* Finch, Steven R. Mathematical constants. Cambridge university press, 2003.
[endsect]

View File

@@ -1,6 +1,6 @@
[section:minimax Minimax Approximations and the Remez Algorithm]
The directory libs/math/minimax contains a command line driven
The directory `libs/math/minimax` contains an interactive 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
@@ -12,10 +12,8 @@ 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].
Unless you already familiar 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:
@@ -41,13 +39,13 @@ 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))
[expression 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
In this case you would define /f/ to return [role serif-italic 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
@@ -156,8 +154,7 @@ Command line options for the program are as follows:
x and y offsets, and of course the coefficients of the polynomials.]]
]
[endsect][/section:minimax Minimax Approximations and the Remez Algorithm]
[endsect] [/section:minimax Minimax Approximations and the Remez Algorithm]
[/
Copyright 2006 John Maddock and Paul A. Bristow.

View File

@@ -24,9 +24,15 @@
template <class I>
polynomial(I first, I last);
template <class U>
explicit polynomial(const U& point);
explicit polynomial(const U& point,
typename boost::enable_if<std::is_convertible<U, T> >::type* = 0);
template <class Range>
explicit polynomial(const Range& r,
typename boost::enable_if<boost::math::tools::detail::is_const_iterable<Range> >::type* = 0); // C++14
polynomial(std::initializer_list<T> l); // C++11
polynomial(std::vector<T>&& p);
// access:
size_type size()const;
size_type degree()const;
@@ -35,7 +41,15 @@
std::vector<T> const& data() const;
std::vector<T>& data();
explicit operator bool() const;
polynomial<T> prime() const;
polynomial<T> integrate() const;
T operator()(T z) const;
// modify:
void set_zero();
@@ -101,8 +115,8 @@
template <class T>
polynomial<T> operator <<= (const U& a);
template <class T>
polynomial<T> operator << (polynomial<T> const &a, const U& b);
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>

View File

@@ -116,7 +116,7 @@ Equivalent to `evaluate_polynomial(poly+1, z*z, count-1) * z + poly[0]`.
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/.
the coefficients stored in /num/ and /denom/.
If the size of the array is specified at runtime then both
polynomials most have order /count-1/ with /count/ coefficients.

View File

@@ -0,0 +1,170 @@
[section:recurrence Tools For 3-Term Recurrence Relations]
[h4 Synopsis]
``
#include <boost/math/tools/recurrence.hpp>
``
namespace boost{ namespace math{ namespace tools{
template <class Recurrence, class T>
T function_ratio_from_backwards_recurrence(const Recurrence& r, const T& factor, boost::uintmax_t& max_iter);
template <class Recurrence, class T>
T function_ratio_from_forwards_recurrence(const Recurrence& r, const T& factor, boost::uintmax_t& max_iter);
template <class NextCoefs, class T>
T apply_recurrence_relation_forward(const NextCoefs& get_coefs, unsigned number_of_steps, T first, T second, long long* log_scaling = 0, T* previous = 0);
template <class T, class NextCoefs>
T apply_recurrence_relation_backward(const NextCoefs& get_coefs, unsigned number_of_steps, T first, T second, long long* log_scaling = 0, T* previous = 0);
template <class Recurrence>
struct forward_recurrence_iterator;
template <class Recurrence>
struct backward_recurrence_iterator;
}}} // namespaces
[h4 Description]
All of the tools in this header require a description of the recurrence relation: this takes the form of
a functor that returns a tuple containing the 3 coefficients, specifically, given a recurrence relation:
[/\Large $$ a_nF_{n-1} + b_nF_n + c_nF_{n+1} = 0 $$]
[equation three_term_recurrence]
And a functor `F` then the expression:
[expression F(n);]
Returns a tuple containing [role serif_italic { a[sub n], b[sub n], c[sub n] }].
For example, the recurrence relation for the Bessel J and Y functions when written in this form is:
[/\Large $$ J_{v-1}(x) - \frac{2v}{x}J_v(x) + J_{v+1}(x)= 0 $$]
[$../equations/three_term_recurrence_bessel_jy.svg]
Therefore, given local variables /x/ and /v/ of type `double` the recurrence relation for Bessel J and Y can be encoded
in a lambda expression like this:
auto recurrence_functor_jy = [&](int n) { return std::make_tuple(1.0, -2 * (v + n) / x, 1.0); };
Similarly, the Bessel I and K recurrence relation differs just by the sign of the final term:
[/\Large $$ I_{v-1}(x) - \frac{2v}{x}I_v(x) - I_{v+1}(x)= 0 $$]
[$../equations/three_term_recurrence_bessel_ik.svg]
And this could be encoded as:
auto recurrence_functor_ik = [&](int n) { return std::make_tuple(1.0, -2 * (v + n) / x, -1.0); };
The tools are then as follows:
template <class Recurrence, class T>
T function_ratio_from_backwards_recurrence(const Recurrence& r, const T& factor, boost::uintmax_t& max_iter);
Given a functor `r` which encodes the recurrence relation for function `F` at some location /n/, then returns the ratio:
[/\Large $$ F_n / F_{n-1} $$]
[$../equations/three_term_recurrence_backwards_ratio.svg]
This calculation is stable only if recurrence is stable in the backwards direction. Further the ratio calculated
is for the dominant solution (in the backwards direction) of the recurrence relation, if there are multiple solutions,
then there is no guarantee that this will find the one you want or expect.
Argument /factor/ is the tolerance required for convergence of the continued fraction associated with
the recurrence relation, and should be no smaller than machine epsilon. Argument /max_iter/ sets
the maximum number of permitted iterations in the associated continued fraction.
template <class Recurrence, class T>
T function_ratio_from_forwards_recurrence(const Recurrence& r, const T& factor, boost::uintmax_t& max_iter);
Given a functor `r` which encodes the recurrence relation for function F at some location /n/, then returns the ratio:
[/\Large $$ F_n / F_{n+1} $$]
[$../equations/three_term_recurrence_forwards_ratio.svg]
This calculation is stable only if recurrence is stable in the forwards direction. Further the ratio calculated
is for the dominant solution (in the forwards direction) of the recurrence relation, if there are multiple solutions,
then there is no guarantee that this will find the one you want or expect.
Argument /factor/ is the tolerance required for convergence of the continued fraction associated with
the recurrence relation, and should be no smaller than machine epsilon. Argument /max_iter/ sets
the maximum number of permitted iterations in the associated continued fraction.
template <class NextCoefs, class T>
T apply_recurrence_relation_forward(const NextCoefs& get_coefs, unsigned number_of_steps, T first, T second, long long* log_scaling = 0, T* previous = 0);
Applies a recurrence relation in a stable forward direction, starting with the values F[sub n-1] and F[sub n].
[variablelist
[[get_coefs] [Functor that returns the coefficients of the recurrence relation. The coefficients should be centered on position /second/.]]
[[number_of_steps][The number of steps to apply the recurrence relation onwards from /second/.]]
[[first] [The value of F[sub n-1]]]
[[second] [The value of F[sub n]]]
[[log_scaling][When provided, the recurrence relations may be rescaled internally to avoid over/underflow issues. The result should be multiplied by `exp(*log_scaling)` to get the true value of the result.]]
[[previous][When provided, is set to the value of F[sub n + number_of_steps - 1]]
]
]
Returns F[sub n + number_of_steps].
template <class NextCoefs, class T>
T apply_recurrence_relation_backward(const NextCoefs& get_coefs, unsigned number_of_steps, T first, T second, long long* log_scaling = 0, T* previous = 0);
Applies a recurrence relation in a stable backward direction, starting with the values F[sub n+1] and F[sub n].
[variablelist
[[get_coefs] [Functor that returns the coefficients of the recurrence relation. The coefficients should be centered on position /second/.]]
[[number_of_steps][The number of steps to apply the recurrence relation backwards from /second/.]]
[[first] [The value of F[sub n+1]]]
[[second] [The value of F[sub n]]]
[[log_scaling][When provided, the recurrence relations may be rescaled internally to avoid over/underflow issues. The result should be multiplied by `exp(*log_scaling)` to get the true value of the result.]]
[[previous][When provided, is set to the value of F[sub n - number_of_steps + 1]]
]
]
Returns F[sub n - number_of_steps].
template <class Recurrence>
struct forward_recurrence_iterator
{
typedef typename boost::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
forward_recurrence_iterator(const Recurrence& r, value_type f_n_minus_1, value_type f_n);
forward_recurrence_iterator(const Recurrence& r, value_type f_n);
/* Operators omitted for clarity */
};
Type `forward_recurrence_iterator` defines a forward-iterator for a recurrence relation stable in the
forward direction. The constructors take the recurrence relation, plus either one or two values: if
only one value is provided, then the second is computed by using the recurrence relation to calculate the function ratio.
template <class Recurrence>
struct backward_recurrence_iterator
{
typedef typename boost::remove_reference<decltype(std::get<0>(std::declval<Recurrence&>()(0)))>::type value_type;
backward_recurrence_iterator(const Recurrence& r, value_type f_n_plus_1, value_type f_n);
backward_recurrence_iterator(const Recurrence& r, value_type f_n);
/* Operators omitted for clarity */
};
Type `backward_recurrence_iterator` defines a forward-iterator for a recurrence relation stable in the
backward direction. The constructors take the recurrence relation, plus either one or two values: if
only one value is provided, then the second is computed by using the recurrence relation to calculate the function ratio.
Note that /incrementing/ this iterator moves the value returned successively to F[sub n-1], F[sub n-2] etc.
[endsect] [/section:recurrence Tools For 3-Term Recurrence Relations]
[/
Copyright 2019 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).
]

View File

@@ -8,7 +8,7 @@
[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`
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>
@@ -82,10 +82,10 @@ you use Boost.Test). This is mainly a debugging/development aid
[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.
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 = {
@@ -129,7 +129,7 @@ Now we can invoke the test function to test tgamma:
// etc ...
//
[endsect][/section:error_test Relative Error and Testing]
[endsect] [/section:error_test Relative Error and Testing]
[/
Copyright 2006 John Maddock and Paul A. Bristow.

View File

@@ -48,7 +48,7 @@ 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
/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.
@@ -81,7 +81,11 @@ that appears in
However, it should be pointed out that there are very few series that require
summation in this way.
[h4 Example]
[h4 Examples]
[import ../../example/series.cpp]
These examples are all in [@../../example/series.cpp]
Let's suppose we want to implement /log(1+x)/ via its infinite series,
@@ -90,48 +94,21 @@ Let's suppose we want to implement /log(1+x)/ via its infinite series,
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;
};
[series_log1p]
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);
}
[series_log1p_func]
[endsect][/section Series Evaluation]
We can almost use the code above for complex numbers as well - unfortunately we need a slightly different
definition for epsilon, and within the functor, mixed complex and integer arithmetic is sadly not supported
(as of C++17), so we need to cast out integers to floats:
[series_clog1p_func]
Of course with a few traits classes and a bit of meta-programming we could fold these two implementations into one, but that's beyond the scope of these examples.
[endsect] [/section Series Evaluation]
[/
Copyright 2006 John Maddock and Paul A. Bristow.

View File

@@ -0,0 +1,62 @@
[/
Copyright Nick Thompson, 2020
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).
]
[section:simple_continued_fraction Simple Continued Fractions]
#include <boost/math/tools/simple_continued_fraction.hpp>
namespace boost::math::tools {
template<typename Real, typename Z = int64_t>
class simple_continued_fraction {
public:
simple_continued_fraction(Real x);
Real khinchin_geometric_mean() const;
Real khinchin_harmonic_mean() const;
template<typename T, typename Z_>
friend std::ostream& operator<<(std::ostream& out, simple_continued_fraction<T, Z>& scf);
};
}
The `simple_continued_fraction` class provided by Boost affords the ability to convert a floating point number into a simple continued fraction.
In addition, we can answer a few questions about the number in question using this representation.
Here's a minimal working example:
using boost::math::constants::pi;
using boost::math::tools::simple_continued_fraction;
auto cfrac = simple_continued_fraction(pi<long double>());
std::cout << "π ≈ " << cfrac << "\n";
// Prints:
// π ≈ [3; 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2]
The class computes partial denominators while simultaneously computing convergents with the modified Lentz's algorithm.
Once a convergent is within a few ulps of the input value, the computation stops.
Note that every floating point number is a rational number, and this exact rational can be exactly converted to a finite continued fraction.
This is perfectly sensible behavior, but we do not do it here.
This is because when examining known values like π, it creates a large number of incorrect partial denominators, even if every bit of the binary representation is correct.
It may be the case the a few incorrect partial convergents is harmless, but we compute continued fractions because we would like to do something with them.
One sensible thing to do it to ask whether the number is in some sense "random"; a question that can be partially answered by computing the Khinchin geometric mean
[$../equations/khinchin_geometric.svg]
and Khinchin harmonic mean
[$../equations/khinchin_harmonic.svg]
If these approach Khinchin's constant /K/[sub 0] and /K/[sub -1] as the number of partial denominators goes to infinity, then our number is "uninteresting" with respect to the characterization.
These violations are washed out if too many incorrect partial denominators are included in the expansion.
Note: The convergence of these means to the Khinchin limit is exceedingly slow; we've used 30,000 decimal digits of π and only found two digits of agreement with /K/[sub 0].
However, clear violations of are obvious, such as the continued fraction expansion of √2, whose Khinchin geometric mean is precisely 2.
[endsect]

View File

@@ -348,13 +348,13 @@ for each element in the tuple (in addition to the input parameters):
result_type operator()(T val)
{
using namespace boost::math::tools;
// estimate the true value, using arbitary precision
// estimate the true value, using arbitrary 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
// Now get the approximation 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);
@@ -394,8 +394,7 @@ This time there's no need to plot a graph, the first few rows are:
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.
So it's pretty clear that this fraction shouldn't be used for small values of /a/ and /z/.
[h4 reference]
@@ -436,7 +435,7 @@ 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]
[endsect] [/section:test_data Graphing, Profiling, and Generating Test Data for Special Functions]
[/
Copyright 2006 John Maddock and Paul A. Bristow.