npstat is hosted by Hepforge, IPPP Durham
NPStat  5.10.0
OSDE1D.hh
Go to the documentation of this file.
1 #ifndef NPSTAT_OSDE1D_HH_
2 #define NPSTAT_OSDE1D_HH_
3 
4 /*!
5 // \file OSDE1D.hh
6 //
7 // \brief Orthogonal Series Density Estimation (OSDE) in one dimension
8 //
9 // Author: I. Volobouev
10 //
11 // May 2017
12 */
13 
14 #include <utility>
15 #include <vector>
16 
19 
20 #ifdef SWIG
21 #include <cassert>
22 #include <stdexcept>
23 
24 #include "npstat/wrap/arrayNDToNumpy.hh"
25 #include "npstat/wrap/numpyArrayUtils.hh"
26 #endif // SWIG
27 
28 namespace npstat {
29  class OSDE1D
30  {
31  public:
32  /**
33  // Main constructor. The arguments are as follows:
34  //
35  // poly1d -- classical orthogonal polynomial system to use
36  //
37  // xmin -- lower bound of the density support region
38  //
39  // xmax -- upper bound of the density support region
40  //
41  // Note that the density estimate will be automatically
42  // normalized for Legendre polynomials only.
43  */
45  double xmin, double xmax);
46 
47  OSDE1D(const OSDE1D&);
48  OSDE1D& operator=(const OSDE1D&);
49 
50  ~OSDE1D();
51 
52  //@{
53  /** Basic inspection of object properties */
54  inline const AbsClassicalOrthoPoly1D& clpoly() const {return *poly_;}
55  inline double xmin() const {return xmin_;}
56  inline double xmax() const {return xmax_;}
57  double weight(double x) const;
58  //@}
59 
60  /** Polynomial values */
61  inline double poly(const unsigned deg, const double x) const
62  {return poly_->poly(deg, (x - shift_)/scale_);}
63 
64  /**
65  // A pair of polynomial values. Faster than
66  // calling "poly" two times.
67  */
68  std::pair<double,double> twopoly(
69  unsigned deg1, unsigned deg2, double x) const;
70 
71  /** Polynomial series representing the fitted density */
72  double series(const double* coeffs, unsigned maxdeg, double x) const;
73 
74  /** Integral of the series */
75  inline double integrateSeries(const double* coeffs,
76  const unsigned maxdeg) const
77  {return scale_*poly_->integrateSeries(coeffs, maxdeg);}
78 
79  /** Unweighted polynomial integral */
80  inline double integratePoly(const unsigned degree,
81  const unsigned power) const
82  {return scale_*poly_->integratePoly(degree, power);}
83 
84  /** Unweighted integral of a product of multiple polynomials */
85  inline double jointIntegral(
86  const unsigned* degrees, unsigned nDegrees) const
87  {return scale_*poly_->jointIntegral(degrees, nDegrees);}
88 
89  /**
90  // Matrix of unweighted pairwise integrals. The returned
91  // matrix will be dimensioned (maxdeg + 1) x (maxdeg + 1).
92  */
93  Matrix<double> tMatrix(unsigned maxdeg) const;
94 
95  /**
96  // Build the coefficients of the orthogonal polynomial series
97  // for the given function. The length of the array "coeffs"
98  // should be at least maxdeg + 1. Note that the coefficients
99  // are returned in the order of increasing degree (same order
100  // is used by the "series" function).
101  */
102  template <class Functor, class Quadrature>
103  void calculateCoeffs(const Functor& fcn, const Quadrature& quad,
104  double* coeffs, unsigned maxdeg) const;
105 
106  /**
107  // Build the coefficients of the orthogonal polynomial series
108  // for the given density using Gauss-Legendre quadratures.
109  // The length of the array "coeffs" should be at least maxdeg + 1.
110  */
111  void densityCoeffs(const AbsDistribution1D& distro,
112  unsigned nIntegrationPoints,
113  double* coeffs, unsigned maxdeg) const;
114 
115  /**
116  // Expected variances of the coefficients obtained previously
117  // with the "densityCoeffs" method. The length of the array
118  // "variances" should be at least maxdeg + 1.
119  */
121  unsigned nIntegrationPoints,
122  const double* coeffs, unsigned maxdeg,
123  double expectedSampleSize,
124  double* variances) const;
125 
126  /**
127  // Expected covariances of the coefficients obtained previously
128  // with the "densityCoeffs" method.
129  */
132  unsigned nIntegrationPoints,
133  const double* coeffs, unsigned maxdeg,
134  double expectedSampleSize) const;
135 
136  /**
137  // Build the coefficients of the orthogonal polynomial series
138  // for the given sample of points (empirical density function).
139  // The length of the array "coeffs" should be at least maxdeg + 1.
140  // Note that the coefficients are returned in the order of
141  // increasing degree (same order is used by the "series" function).
142  */
143  template <class Numeric>
144  void sampleCoeffs(const Numeric* coords, unsigned long lenCoords,
145  double* coeffs, unsigned maxdeg) const;
146 
147  /**
148  // Estimate variances of the coefficients obtained previously
149  // with the "sampleCoeffs" method.
150  */
151  template <class Numeric>
152  void sampleCoeffsVariance(const Numeric* coords, unsigned long lenCoords,
153  const double* coeffs, unsigned maxdeg,
154  double* variances) const;
155 
156  /**
157  // Estimate covariances of the coefficients obtained previously
158  // with the "sampleCoeffs" method.
159  */
160  template <class Numeric>
162  sampleCoeffsCovariance(const Numeric* coords, unsigned long lenCoords,
163  const double* coeffs, unsigned maxdeg) const;
164 
165  /**
166  // Build the coefficients of the orthogonal polynomial series for
167  // the given sample of weighted points (empirical density function).
168  // The first element of the pair will be treated as the coordinate
169  // and the second element will be treated as weight. Weights must
170  // not be negative. The length of the array "coeffs" should be
171  // at least maxdeg + 1. Note that the coefficients are returned
172  // in the order of increasing degree (same order is used by the
173  // "series" function).
174  */
175  template <class Pair>
176  void weightedSampleCoeffs(const Pair* points, unsigned long numPoints,
177  double* coeffs, unsigned maxdeg) const;
178 
179  /**
180  // Estimate variances of the coefficients obtained previously
181  // with the "weightedSampleCoeffs" method. This code assumes that
182  // weights and coordinates are statistically independent from
183  // each other.
184  */
185  template <class Pair>
186  void weightedSampleCoeffsVariance(const Pair* points, unsigned long nPoints,
187  const double* coeffs, unsigned maxdeg,
188  double* variances) const;
189 
190  /**
191  // Estimate covariances of the coefficients obtained previously
192  // with the "weightedSampleCoeffs" method. This code assumes that
193  // weights and coordinates are statistically independent from
194  // each other.
195  */
196  template <class Pair>
198  weightedSampleCoeffsCovariance(const Pair* points, unsigned long nPoints,
199  const double* coeffs, unsigned maxdeg) const;
200 
201  /**
202  // Integrated squared error between the given function and the
203  // polynomial series
204  */
205  template <class Functor, class Quadrature>
206  double integratedSquaredError(const Functor& fcn,
207  const Quadrature& quad,
208  const double* coeffs,
209  unsigned maxdeg) const;
210 
211  /**
212  // Integrated squared error between the given function and the
213  // polynomial series on an arbitrary interval. If the given
214  // interval has regions beyond xmin() and xmax(), the integration
215  // will assume that on those regions the value of the series is 0,
216  // and the quadrature will be carried out there separately.
217  */
218  template <class Functor, class Quadrature>
219  double integratedSquaredError(const Functor& fcn,
220  const Quadrature& quad,
221  const double* coeffs,
222  unsigned maxdeg,
223  double xmin, double xmax) const;
224 
225  /**
226  // A helper function for choosing the density support interval if
227  // it is unknown and has to be estimated from the sample. If the
228  // limit is not from the sample (as indicated by the corresponding
229  // boolean argument), it is left unchanged. The first element of
230  // the returned pair will correspond to the lower limit of the
231  // interval and the second element to the upper.
232  */
233  static std::pair<double,double> supportRegion(
234  const AbsClassicalOrthoPoly1D& poly1d,
235  double leftLimit, bool leftIsFromSample,
236  double rightLimit, bool rightIsFromSample,
237  unsigned long nPoints);
238 
239  /**
240  // Optimal OSDE truncation degree according to the Hart scheme.
241  // Minimizes Sum_{i=0}^M (k*variances[i] - coeffs[i]^2) over M.
242  // The default value of k = 2 corresponds to the original scheme.
243  // Argument arrays "coeffs" and "variances" should have at least
244  // maxdeg + 1 elements and should be calculated in advance
245  // with "sampleCoeffs"/"sampleCoeffsVariance" or with
246  // "weightedSampleCoeffs"/"weightedSampleCoeffsVariance" methods.
247  */
248  static unsigned optimalDegreeHart(const double* coeffs,
249  const double* variances,
250  unsigned maxdeg, double k = 2.0);
251 
252  private:
253  const AbsClassicalOrthoPoly1D* poly_;
254  double xmin_;
255  double xmax_;
256  double shift_;
257  double scale_;
258 
259 #ifdef SWIG
260  public:
261  inline double integrateSeries_2(
262  const double* coeffs, unsigned nCoeffs) const
263  {
264  assert(nCoeffs);
265  return scale_*poly_->integrateSeries(coeffs, nCoeffs-1U);
266  }
267 
268  inline double series_2(const double* coeffs, unsigned nCoeffs,
269  const double x) const
270  {
271  assert(nCoeffs);
272  return this->series(coeffs, nCoeffs-1U, x);
273  }
274 
275  inline PyObject* seriesScan(const double* coeffs, unsigned nCoeffs,
276  const unsigned nOut) const
277  {
278  assert(nCoeffs);
279  const unsigned maxdeg = nCoeffs - 1U;
280  const int typenum = NumpyTypecode<double>::code;
281  npy_intp sh = nOut;
282  PyObject* xarr = PyArray_SimpleNew(1, &sh, typenum);
283  PyObject* yarr = PyArray_SimpleNew(1, &sh, typenum);
284  if (xarr && yarr)
285  {
286  if (nOut)
287  {
288  try
289  {
290  double* x = (double*)PyArray_DATA((PyArrayObject*)xarr);
291  double* y = (double*)PyArray_DATA((PyArrayObject*)yarr);
292  const double dx = (xmax_ - xmin_)/nOut;
293  for (unsigned i=0; i<nOut; ++i)
294  {
295  x[i] = xmin_ + (i + 0.5)*dx;
296  y[i] = this->series(coeffs, maxdeg, x[i]);
297  }
298  }
299  catch (const std::exception& e)
300  {
301  Py_DECREF(xarr);
302  Py_DECREF(yarr);
303  throw e;
304  }
305  }
306  return Py_BuildValue("(OO)", xarr, yarr);
307  }
308  else
309  {
310  Py_XDECREF(xarr);
311  Py_XDECREF(yarr);
312  return 0;
313  }
314  }
315 
316  inline void densityCoeffs_2(const AbsDistribution1D& distro,
317  unsigned nIntegrationPoints,
318  double* coeffsOut, unsigned nCoeffsOut) const
319  {
320  assert(nCoeffsOut);
321  this->densityCoeffs(distro, nIntegrationPoints, coeffsOut, nCoeffsOut-1);
322  }
323 
324  inline void densityCoeffsVariance_2(const AbsDistribution1D& distro,
325  unsigned nIntegrationPoints,
326  const double* coeffs, unsigned nCoeffs,
327  double expectedSampleSize,
328  double* coeffsOut, unsigned nCoeffsOut) const
329  {
330  assert(nCoeffs);
331  if (!(nCoeffs == nCoeffsOut)) throw std::invalid_argument(
332  "In npstat::OSDE1D::densityCoeffsVariance_2: incompatible output array");
333  this->densityCoeffsVariance(distro, nIntegrationPoints, coeffs,
334  nCoeffs-1U, expectedSampleSize, coeffsOut);
335  }
336 
338  densityCoeffsCovariance_2(const AbsDistribution1D& distro,
339  unsigned nIntegrationPoints,
340  const double* coeffs, unsigned nCoeffs,
341  double expectedSampleSize) const
342  {
343  assert(nCoeffs);
344  return this->densityCoeffsCovariance(distro, nIntegrationPoints,
345  coeffs, nCoeffs-1, expectedSampleSize);
346  }
347 
348  template <class Vec>
349  inline void sampleCoeffs_2(const Vec& sample,
350  double* coeffsOut, unsigned nCoeffsOut) const
351  {
352  const unsigned long sz = sample.size();
353  const typename Vec::value_type *in = 0;
354  if (sz)
355  in = &sample[0];
356  assert(nCoeffsOut);
357  this->sampleCoeffs(in, sz, coeffsOut, nCoeffsOut-1U);
358  }
359 
360  template <class Vec>
361  inline void sampleCoeffsVariance_2(const Vec& sample,
362  const double *coeffs, unsigned nCoeffs,
363  double* coeffsOut, unsigned nCoeffsOut) const
364  {
365  const unsigned long sz = sample.size();
366  const typename Vec::value_type *in = 0;
367  if (sz)
368  in = &sample[0];
369  assert(nCoeffs);
370  if (!(nCoeffs == nCoeffsOut)) throw std::invalid_argument(
371  "In npstat::OSDE1D::sampleCoeffsVariance_2: incompatible output array");
372  this->sampleCoeffsVariance(in, sz, coeffs, nCoeffs-1U, coeffsOut);
373  }
374 
375  template <class Vec>
376  inline npstat::Matrix<double> sampleCoeffsCovariance_2(
377  const Vec& sample, const double *coeffs, unsigned nCoeffs) const
378  {
379  const unsigned long sz = sample.size();
380  const typename Vec::value_type *in = 0;
381  if (sz)
382  in = &sample[0];
383  assert(nCoeffs);
384  return this->sampleCoeffsCovariance(in, sz, coeffs, nCoeffs-1U);
385  }
386 
387  template <class Quadrature>
388  inline void calculateCoeffs_2(const AbsDistribution1D& d,
389  const Quadrature& quad,
390  double* coeffsOut, unsigned nCoeffsOut) const
391  {
392  DensityFunctor1D fcn(d);
393  assert(nCoeffsOut);
394  return this->calculateCoeffs(fcn, quad, coeffsOut, nCoeffsOut-1U);
395  }
396 
397  template <class Quadrature>
398  inline double integratedSquaredError_2(const AbsDistribution1D& d,
399  const Quadrature& quad,
400  const double* coeffs, unsigned nCoeffs) const
401  {
402  DensityFunctor1D fcn(d);
403  assert(nCoeffs);
404  return this->integratedSquaredError(fcn, quad, coeffs, nCoeffs-1U);
405  }
406 
407  template <class Vec>
408  inline void weightedSampleCoeffs_2(const Vec& sample,
409  double* coeffsOut, unsigned nCoeffsOut) const
410  {
411  const unsigned long sz = sample.size();
412  const typename Vec::value_type *in = 0;
413  if (sz)
414  in = &sample[0];
415  assert(nCoeffsOut);
416  this->weightedSampleCoeffs(in, sz, coeffsOut, nCoeffsOut-1U);
417  }
418 
419  template <class Vec>
420  inline npstat::Matrix<double> weightedSampleCoeffsCovariance_2(
421  const Vec& sample, const double *coeffs, unsigned nCoeffs) const
422  {
423  const unsigned long sz = sample.size();
424  const typename Vec::value_type *in = 0;
425  if (sz)
426  in = &sample[0];
427  assert(nCoeffs);
428  return this->weightedSampleCoeffsCovariance(in, sz, coeffs, nCoeffs-1U);
429  }
430 
431  template <class Vec>
432  inline void
433  weightedSampleCoeffsVariance_2(const Vec& sample,
434  const double *coeffs, unsigned nCoeffs,
435  double* coeffsOut, unsigned nCoeffsOut) const
436  {
437  const unsigned long sz = sample.size();
438  const typename Vec::value_type *in = 0;
439  if (sz)
440  in = &sample[0];
441  assert(nCoeffs);
442  if (!(nCoeffs == nCoeffsOut)) throw std::invalid_argument(
443  "In npstat::OSDE1D::weightedSampleCoeffsVariance_2: incompatible output array");
444  this->weightedSampleCoeffsVariance(in, sz, coeffs, nCoeffs-1U, coeffsOut);
445  }
446 
447  inline static unsigned optimalDegreeHart_2(const double* coeffs, unsigned nCoeffs,
448  const double* variances, unsigned nVariances,
449  double k = 2.0)
450  {
451  assert(nCoeffs);
452  if (!(nCoeffs == nVariances)) throw std::invalid_argument(
453  "In npstat::OSDE1D::optimalDegreeHart_2: incompatible input arrays");
454  return optimalDegreeHart(coeffs, variances, nCoeffs-1U, k);
455  }
456 #endif // SWIG
457  };
458 }
459 
460 #include "npstat/stat/OSDE1D.icc"
461 
462 #endif // NPSTAT_OSDE1D_HH_
Base class for classical continuous orthonormal polynomials.
Interface definition for 1-d continuous statistical distributions.
Definition: AbsClassicalOrthoPoly1D.hh:32
double jointIntegral(const unsigned *degrees, unsigned nDegrees) const
double integrateSeries(const double *coeffs, unsigned maxdeg) const
long double poly(unsigned deg, long double x) const
double integratePoly(unsigned degree, unsigned power) const
Definition: OSDE1D.hh:30
Matrix< double > tMatrix(unsigned maxdeg) const
double integrateSeries(const double *coeffs, const unsigned maxdeg) const
Definition: OSDE1D.hh:75
static unsigned optimalDegreeHart(const double *coeffs, const double *variances, unsigned maxdeg, double k=2.0)
double integratePoly(const unsigned degree, const unsigned power) const
Definition: OSDE1D.hh:80
double poly(const unsigned deg, const double x) const
Definition: OSDE1D.hh:61
npstat::Matrix< double > densityCoeffsCovariance(const AbsDistribution1D &distro, unsigned nIntegrationPoints, const double *coeffs, unsigned maxdeg, double expectedSampleSize) const
void densityCoeffs(const AbsDistribution1D &distro, unsigned nIntegrationPoints, double *coeffs, unsigned maxdeg) const
void weightedSampleCoeffsVariance(const Pair *points, unsigned long nPoints, const double *coeffs, unsigned maxdeg, double *variances) const
npstat::Matrix< double > weightedSampleCoeffsCovariance(const Pair *points, unsigned long nPoints, const double *coeffs, unsigned maxdeg) const
void sampleCoeffs(const Numeric *coords, unsigned long lenCoords, double *coeffs, unsigned maxdeg) const
static std::pair< double, double > supportRegion(const AbsClassicalOrthoPoly1D &poly1d, double leftLimit, bool leftIsFromSample, double rightLimit, bool rightIsFromSample, unsigned long nPoints)
std::pair< double, double > twopoly(unsigned deg1, unsigned deg2, double x) const
void calculateCoeffs(const Functor &fcn, const Quadrature &quad, double *coeffs, unsigned maxdeg) const
void densityCoeffsVariance(const AbsDistribution1D &distro, unsigned nIntegrationPoints, const double *coeffs, unsigned maxdeg, double expectedSampleSize, double *variances) const
double integratedSquaredError(const Functor &fcn, const Quadrature &quad, const double *coeffs, unsigned maxdeg, double xmin, double xmax) const
void sampleCoeffsVariance(const Numeric *coords, unsigned long lenCoords, const double *coeffs, unsigned maxdeg, double *variances) const
double integratedSquaredError(const Functor &fcn, const Quadrature &quad, const double *coeffs, unsigned maxdeg) const
OSDE1D(const AbsClassicalOrthoPoly1D &poly1d, double xmin, double xmax)
const AbsClassicalOrthoPoly1D & clpoly() const
Definition: OSDE1D.hh:54
void weightedSampleCoeffs(const Pair *points, unsigned long numPoints, double *coeffs, unsigned maxdeg) const
double jointIntegral(const unsigned *degrees, unsigned nDegrees) const
Definition: OSDE1D.hh:85
npstat::Matrix< double > sampleCoeffsCovariance(const Numeric *coords, unsigned long lenCoords, const double *coeffs, unsigned maxdeg) const
double series(const double *coeffs, unsigned maxdeg, double x) const
Definition: AbsArrayProjector.hh:14
Definition: AbsDistribution1D.hh:31