You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
5.6 KiB
238 lines
5.6 KiB
/*
|
|
* tests/quadt.cpp
|
|
*
|
|
* This work was supported by the Director, Office of Science, Division
|
|
* of Mathematical, Information, and Computational Sciences of the
|
|
* U.S. Department of Energy under contract number DE-AC03-76SF00098.
|
|
*
|
|
* Copyright (c) 2000-2001
|
|
*
|
|
* This contains a C++ class template for a tanh-sinh quadrature
|
|
* algorithm, which employs the transformation
|
|
*
|
|
* t <-- tanh (sinh (x))
|
|
*
|
|
* This quadrature scheme is suitable for any function that is
|
|
* continuous, infinitely differentiable and integrable on a finite
|
|
* open interval. It can also be used for certain integrals on
|
|
* infinite intervals by making a suitable change of variable.
|
|
* While this routine is not quite as efficient as Gaussian quadrature,
|
|
* it can be used for functions with an integrable singularity at one
|
|
* or both of the endpoints. Further, this scheme has the advantage
|
|
* that function evaluation at one level are all utilized at the next
|
|
* level, thus saving significant computation.
|
|
*
|
|
* This program is based on David Bailey's tquadt.f program (written
|
|
* in Fortran 90). C++ conversion, quad-double precision support,
|
|
* and few other changes have been added.
|
|
*/
|
|
#ifndef _QUADT_CC_
|
|
#define _QUADT_CC_
|
|
|
|
/* Suppose we are given the integral
|
|
*
|
|
* / 1
|
|
* I = | f(x) dx
|
|
* / -1
|
|
*
|
|
* Then the substitution t = tanh (sinh (x)) gives
|
|
*
|
|
* dt = (tanh (sinh (x))' dx = (sech (sinh (x)))^2 * cosh(x) dx
|
|
*
|
|
* Also the limit point x = 1 corresponds to t =
|
|
*/
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
#include <cmath>
|
|
|
|
#include <qd/qd_real.h>
|
|
|
|
template<class T>
|
|
class quadt {
|
|
public:
|
|
/* Constructor. This will create a tanh-sinh quadrature class.
|
|
Parameters
|
|
eps -- The machine epsilon of the variable type to be used. */
|
|
quadt(double eps);
|
|
|
|
/* Destructor. This will take care of disposing internal tables, etc. */
|
|
~quadt();
|
|
|
|
/* Computes the integral of the function f from -1 to 1.
|
|
The class F is any class with overloaded operator() (T &). */
|
|
template <class F>
|
|
int integrate_u(const F &f, double tol, T &result, double &err);
|
|
|
|
/* Computes the integral of the function f from a to b.
|
|
The class F is any class with overloaded operator() (T &). */
|
|
template <class F>
|
|
int integrate(const F &f, T a, T b, double tol, T &result, double &err);
|
|
|
|
private:
|
|
int max_level;
|
|
double initial_width, final_width;
|
|
int table_size;
|
|
double eps;
|
|
|
|
/* Pre-computed quadrature points. */
|
|
T *weights;
|
|
T *points;
|
|
|
|
/* Scales and translates the given function f from the
|
|
interval [a, b] to [-1, 1] so it can be evaluated using
|
|
the tanh-sinh substitution. */
|
|
template <class F>
|
|
class UnitFunction {
|
|
private:
|
|
F f;
|
|
T offset, h;
|
|
public:
|
|
UnitFunction(const F &f, const T &a, const T &b) : f(f) {
|
|
offset = 0.5 * (a + b);
|
|
h = (b - a) * 0.5;
|
|
}
|
|
T operator()(T x) const {
|
|
return f(offset + h * x) * h;
|
|
}
|
|
};
|
|
|
|
/* Initializes the weight and abcissa table. */
|
|
void init_table();
|
|
};
|
|
|
|
|
|
|
|
/*-**** Class Template Implementations ****-*/
|
|
template <class T>
|
|
quadt<T>::quadt(double eps) {
|
|
max_level = 11;
|
|
initial_width = 0.5;
|
|
final_width = std::ldexp(initial_width, -max_level+1);
|
|
table_size = static_cast<int>(2.0 * 7.0 / final_width);
|
|
this->eps = eps;
|
|
|
|
init_table();
|
|
}
|
|
|
|
template <class T>
|
|
quadt<T>::~quadt() {
|
|
delete [] weights;
|
|
delete [] points;
|
|
}
|
|
|
|
template <class T>
|
|
void quadt<T>::init_table() {
|
|
|
|
weights = new T[table_size];
|
|
points = new T[table_size];
|
|
|
|
double h = initial_width * 2.0;
|
|
double dt;
|
|
double t;
|
|
int i = 0;
|
|
T sinh_t, cosh_t, sinh_s, cosh_s;
|
|
T x, w;
|
|
for (int level = 1; level <= max_level; level++, h *= 0.5) {
|
|
t = h * 0.5;
|
|
dt = (level == 1) ? t : h;
|
|
for (;; t += dt) {
|
|
sincosh(T(t), sinh_t, cosh_t);
|
|
sincosh(sinh_t, sinh_s, cosh_s);
|
|
x = sinh_s / cosh_s;
|
|
// w = (cosh_t / cosh_s) / cosh_s;
|
|
w = (cosh_t / sqr(cosh_s));
|
|
|
|
if (x == 1.0 || w < eps) {
|
|
weights[i++] = 0.0;
|
|
break;
|
|
}
|
|
|
|
points[i] = x;
|
|
weights[i] = w;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
template <class T> template <class F>
|
|
int quadt<T>::integrate_u(const F &f, double tol,
|
|
T &result, double &err) {
|
|
T r1, r2, r3, s;
|
|
T x, w;
|
|
int level;
|
|
double h = initial_width;
|
|
bool conv = false;
|
|
int i = 0;
|
|
|
|
r1 = r2 = r3 = 0.0;
|
|
s = f(T(0.0));
|
|
for (level = 1; level <= max_level; level++, h *= 0.5) {
|
|
|
|
/* Compute the integral */
|
|
for (;;) {
|
|
x = points[i];
|
|
w = weights[i];
|
|
i++;
|
|
if (w == 0.0)
|
|
break;
|
|
s += w * (f(x) + f(-x));
|
|
}
|
|
|
|
r1 = s * h;
|
|
|
|
/* Check for convergence. */
|
|
if (level > 2) {
|
|
double e1, e2, d1, d2;
|
|
|
|
e1 = abs(to_double(r1 - r2));
|
|
if (e1 == 0.0)
|
|
err = eps;
|
|
else {
|
|
e2 = abs(to_double(r1 - r3));
|
|
d1 = log(e1);
|
|
d2 = log(e2);
|
|
|
|
err = exp(d1 * d1 / d2);
|
|
}
|
|
|
|
cout << " level = " << level << endl;
|
|
cout << " r = " << r1 << endl;
|
|
cout << " err = " << err << endl;
|
|
|
|
if (err < abs(r1) * tol) {
|
|
conv = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
r2 = r1;
|
|
r3 = r2;
|
|
}
|
|
|
|
if (level > max_level)
|
|
puts("Level exhausted.");
|
|
|
|
result = r1;
|
|
if (!conv) {
|
|
/* No convergence. */
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
template <class T> template <class F>
|
|
int quadt<T>::integrate(const F &f, T a, T b, double tol,
|
|
T &result, double &err) {
|
|
if (a == -1.0 && b == 1.0)
|
|
return integrate_u(f, tol, result, err);
|
|
else {
|
|
UnitFunction<F> unit_f(f, a, b);
|
|
return integrate_u< UnitFunction<F> >(unit_f, tol, result, err);
|
|
}
|
|
}
|
|
|
|
#endif /* _QUADT_CC_ */
|
|
|
|
|