dynare/dynare++/integ/cc/quadrature.hweb

312 lines
10 KiB
Plaintext

@q $Id: quadrature.hweb 2269 2008-11-23 14:33:22Z michel $ @>
@q Copyright 2005, Ondra Kamenik @>
@*2 Quadrature. This is {\tt quadrature.h} file
This file defines an interface for one dimensional (non-nested) quadrature
|OneDQuadrature|, and a parent for all multi-dimensional
quadratures. This parent class |Quadrature| presents a general concept of
quadrature, this is
$$\int f(x){\rm d}x \approx\sum_{i=1}^N w_ix_i$$
The class |Quadrature| just declares this concept. The concept is
implemented by class |QuadratureImpl| which paralelizes the
summation. All implementations therefore wishing to use the parallel
implementation should inherit from |QuadratureImpl| and integration is
done.
The integration concept relies on a point iterator, which goes through
all $x_i$ and $w_i$ for $i=1,\ldots,N$. All the iterators must be able
to go through only a portion of the set $i=1,\ldots,N$. This enables
us to implement paralelism, for two threads for example, one iterator
goes from the beginning to the (approximately) half, and the other
goes from the half to the end.
Besides this concept of the general quadrature, this file defines also
one dimensional quadrature, which is basically a scheme of points and
weights for different levels. The class |OneDQuadrature| is a parent
of all such objects, the classes |GaussHermite| and |GaussLegendre|
are specific implementations for Gauss--Hermite and Gauss--Legendre
quadratures resp.
@s OneDQuadrature int
@s Quadrature int
@s IntegrationWorker int
@s QuadratureImpl int
@s OneDPrecalcQuadrature int
@s GaussHermite int
@s GaussLegendre int
@s NormalICDF int
@s _Tpit int
@c
#ifndef QUADRATURE_H
#define QUADRATURE_H
#include <cstdlib>
#include "vector_function.h"
#include "int_sequence.h"
#include "sthread.h"
@<|OneDQuadrature| class declaration@>;
@<|Quadrature| class declaration@>;
@<|IntegrationWorker| class declaration@>;
@<|QuadratureImpl| class declaration@>;
@<|OneDPrecalcQuadrature| class declaration@>;
@<|GaussHermite| class declaration@>;
@<|GaussLegendre| class declaration@>;
@<|NormalICDF| class declaration@>;
#endif
@ This pure virtual class represents a concept of one-dimensional
(non-nested) quadrature. So, one dimensional quadrature must return
number of levels, number of points in a given level, and then a point
and a weight in a given level and given order.
@<|OneDQuadrature| class declaration@>=
class OneDQuadrature {
public:@;
virtual ~OneDQuadrature()@+ {}
virtual int numLevels() const =0;
virtual int numPoints(int level) const =0;
virtual double point(int level, int i) const =0;
virtual double weight(int lelel, int i) const =0;
};
@ This is a general concept of multidimensional quadrature. at this
general level, we maintain only a dimension, and declare virtual
functions for integration. The function take two forms; first takes a
constant |VectorFunction| as an argument, creates locally
|VectorFunctionSet| and do calculation, second one takes as an
argument |VectorFunctionSet|.
Part of the interface is a method returning a number of evaluations
for a specific level. Note two things: this assumes that the number of
evaluations is known apriori and thus it is not applicable for
adaptive quadratures, second for Monte Carlo type of quadrature, the
level is a number of evaluations.
@<|Quadrature| class declaration@>=
class Quadrature {
protected:@;
int dim;
public:@;
Quadrature(int d) : dim(d)@+ {}
virtual ~Quadrature()@+ {}
int dimen() const
{@+ return dim;@+}
virtual void integrate(const VectorFunction& func, int level,
int tn, Vector& out) const =0;
virtual void integrate(VectorFunctionSet& fs, int level, Vector& out) const =0;
virtual int numEvals(int level) const =0;
};
@ This is just an integration worker, which works over a given
|QuadratureImpl|. It also needs the function, level, a specification
of the subgroup of points, and output vector.
See |@<|QuadratureImpl| class declaration@>| for details.
@<|IntegrationWorker| class declaration@>=
template <typename _Tpit>
class QuadratureImpl;
template <typename _Tpit>
class IntegrationWorker : public THREAD {
const QuadratureImpl<_Tpit>& quad;
VectorFunction& func;
int level;
int ti;
int tn;
Vector& outvec;
public:@;
IntegrationWorker(const QuadratureImpl<_Tpit>& q, VectorFunction& f, int l,
int tii, int tnn, Vector& out)
: quad(q), func(f), level(l), ti(tii), tn(tnn), outvec(out) @+{}
@<|IntegrationWorker::operator()()| code@>;
};
@ This integrates the given portion of the integral. We obtain first
and last iterators for the portion (|beg| and |end|). Then we iterate
through the portion. and finally we add the intermediate result to the
result |outvec|.
This method just everything up as it is coming. This might be imply
large numerical errors, perhaps in future I will implement something
smarter.
@<|IntegrationWorker::operator()()| code@>=
void operator()() {
_Tpit beg = quad.begin(ti, tn, level);
_Tpit end = quad.begin(ti+1, tn, level);
Vector tmpall(outvec.length());
tmpall.zeros();
Vector tmp(outvec.length());
// note that since beg came from begin, it has empty signal
// and first evaluation gets no signal
for (_Tpit run = beg; run != end; ++run) {
func.eval(run.point(), run.signal(), tmp);
tmpall.add(run.weight(), tmp);
}
{
SYNCHRO@, syn(&outvec, "IntegrationWorker");
outvec.add(1.0, tmpall);
}
}
@ This is the class which implements the integration. The class is
templated by the iterator type. We declare a method |begin| returning
an iterator to the beginnning of the |ti|-th portion out of total |tn|
portions for a given level.
In addition, we define a method which saves all the points to a given
file. Only for debugging purposes.
@<|QuadratureImpl| class declaration@>=
template <typename _Tpit>
class QuadratureImpl : public Quadrature {
friend class IntegrationWorker<_Tpit>;
public:@;
QuadratureImpl(int d) : Quadrature(d)@+ {}
@<|QuadratureImpl::integrate| code@>;
void integrate(const VectorFunction& func,
int level, int tn, Vector& out) const {
VectorFunctionSet fs(func, tn);
integrate(fs, level, out);
}
@<|Quadrature::savePoints| code@>;
_Tpit start(int level) const
{@+ return begin(0,1,level);@+}
_Tpit end(int level) const
{@+ return begin(1,1,level);@+}
protected:@;
virtual _Tpit begin(int ti, int tn, int level) const =0;
};
@ Just fill a thread group with workes and run it.
@<|QuadratureImpl::integrate| code@>=
void integrate(VectorFunctionSet& fs, int level, Vector& out) const {
// todo: out.length()==func.outdim()
// todo: dim == func.indim()
out.zeros();
THREAD_GROUP@, gr;
for (int ti = 0; ti < fs.getNum(); ti++) {
gr.insert(new IntegrationWorker<_Tpit>(*this, fs.getFunc(ti),
level, ti, fs.getNum(), out));
}
gr.run();
}
@ Just for debugging.
@<|Quadrature::savePoints| code@>=
void savePoints(const char* fname, int level) const
{
FILE* fd;
if (NULL==(fd = fopen(fname,"w"))) {
// todo: raise
fprintf(stderr, "Cannot open file %s for writing.\n", fname);
exit(1);
}
_Tpit beg = begin(0,1,level);
_Tpit end = begin(1,1,level);
for (_Tpit run = beg; run != end; ++run) {
fprintf(fd, "%16.12g", run.weight());
for (int i = 0; i < dimen(); i++)
fprintf(fd, "\t%16.12g", run.point()[i]);
fprintf(fd, "\n");
}
fclose(fd);
}
@ This is only an interface to a precalculated data in file {\tt
precalc\_quadrature.dat} which is basically C coded static data. It
implements |OneDQuadrature|. The data file is supposed to define the
following data: number of levels, array of number of points at each
level, an array of weights and array of points. The both latter array
store data level by level. An offset for a specific level is stored in
|offsets| integer sequence.
The implementing subclasses just fill the necessary data from the
file, the rest is calculated here.
@<|OneDPrecalcQuadrature| class declaration@>=
class OneDPrecalcQuadrature : public OneDQuadrature {
int num_levels;
const int* num_points;
const double* weights;
const double* points;
IntSequence offsets;
public:@;
OneDPrecalcQuadrature(int nlevels, const int* npoints,
const double* wts, const double* pts)
: num_levels(nlevels), num_points(npoints),
weights(wts), points(pts), offsets(num_levels)
{@+ calcOffsets();@+}
virtual ~OneDPrecalcQuadrature()@+ {}
int numLevels() const
{@+ return num_levels;@+}
int numPoints(int level) const
{@+ return num_points[level-1];@+}
double point(int level, int i) const
{@+ return points[offsets[level-1]+i];@+}
double weight(int level, int i) const
{@+ return weights[offsets[level-1]+i];@+}
protected:@;
void calcOffsets();
};
@ Just precalculated Gauss--Hermite quadrature. This quadrature integrates exactly integrals
$$\int_{-\infty}^{\infty} x^ke^{-x^2}{\rm d}x$$
for level $k$.
Note that if pluging this one-dimensional quadrature to product or
Smolyak rule in order to integrate a function $f$ through normally
distributed inputs, one has to wrap $f$ to
|GaussConverterFunction| and apply the product or Smolyak rule to the
new function.
Check {\tt precalc\_quadrature.dat} for available levels.
@<|GaussHermite| class declaration@>=
class GaussHermite : public OneDPrecalcQuadrature {
public:@;
GaussHermite();
};
@ Just precalculated Gauss--Legendre quadrature. This quadrature integrates exactly integrals
$$\int_0^1x^k{\rm d}x$$
for level $k$.
Check {\tt precalc\_quadrature.dat} for available levels.
@<|GaussLegendre| class declaration@>=
class GaussLegendre : public OneDPrecalcQuadrature {
public:@;
GaussLegendre();
};
@ This is just an inverse cummulative density function of normal
distribution. Its only method |get| returns for a given number
$x\in(0,1)$ a number $y$ such that $P(z<y)=x$, where the probability
is taken over normal distribution $N(0,1)$.
Currently, the implementation is done by a table lookup which implies
that the tails had to be chopped off. This further implies that Monte
Carlo quadratures using this transformation to draw points from
multidimensional $N(0,I)$ fail to integrate with satisfactory
precision polynomial functions, for which the tails matter.
@<|NormalICDF| class declaration@>=
class NormalICDF {
public:@;
static double get(double x);
};
@ End of {\tt quadrature.h} file