LALPulsar  6.1.0.1-89842e6
ProbabilityDensity.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010 Reinhard Prix
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with with program; see the file COPYING. If not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  */
19 
20 /*********************************************************************************/
21 /**
22  * \author R. Prix
23  * \file
24  * \brief
25  * Module implementing a probability density function (pdf) object,
26  * and useful methods to operate on such objects.
27  *
28  */
29 #include "config.h"
30 
31 /* System includes */
32 #include <math.h>
33 
34 /* gsl includes */
35 #include <gsl/gsl_math.h>
36 
37 /* LAL-includes */
38 #include <lal/XLALError.h>
39 #include <lal/AVFactories.h>
40 #include <lal/LALMalloc.h>
41 #include <lal/LALStdlib.h>
42 
43 #include <lal/ProbabilityDensity.h>
44 
45 /* ----- MACRO definitions ---------- */
46 
47 /* ---------- internal type definitions ---------- */
48 
49 /* ---------- internal prototypes ---------- */
50 /* empty struct initializers */
51 
52 
53 /* ==================== function definitions ==================== */
54 /**
55  * Function to generate random samples drawn from the given pdf(x)
56  * NOTE: if the 'sampling' field is NULL, it will be set the first call to this function.
57  */
58 REAL8
59 XLALDrawFromPDF1D( pdf1D_t *pdf, /**< [in] probability density to sample from */
60  const gsl_rng *rng /**< random-number generator */
61  )
62 {
63  /* check input consistency */
64  if ( !pdf || !rng ) {
65  XLALPrintError( "%s: NULL input 'pdf = %p' or 'rng = %p'\n", __func__, pdf, rng );
67  }
68  if ( XLALCheckValidPDF1D( pdf ) != XLAL_SUCCESS ) {
69  XLALPrintError( "%s: invalid pdf.\n", __func__ );
71  }
72 
73  /* ----- special case 1: single value with certainty */
74  if ( pdf->xTics->length == 1 ) {
75  return pdf->xTics->data[0];
76  }
77 
78  /* ----- special case 2: uniform pdf in [xMin, xMax] */
79  if ( pdf->xTics->length == 2 ) {
80  return gsl_ran_flat( rng, pdf->xTics->data[0], pdf->xTics->data[1] );
81  }
82 
83  /* ----- general case: draw from discretized pdf ----- */
84 
85 
86  // ----- buffer gsl_ran_discrete_t if not computed previously
87  if ( pdf->sampling == NULL ) {
88  // first need to prepare discrete *probabilities* prob[i] from probability *density* function pdf[i]
89  // namely simply using the respective bin-sizes xBin[i]: prob[i] = pdf[i] * xBin[i]
90  UINT4 numBins = pdf->xTics->length - 1;
91  REAL8 *prob;
92  if ( ( prob = XLALMalloc( numBins * sizeof( *prob ) ) ) == NULL ) {
93  XLALPrintError( "%s: failed to XLALMalloc %d-array of REAL8s\n", __func__, numBins );
95  }
96  UINT4 i;
97  for ( i = 0; i < numBins; i ++ ) {
98  REAL8 dx_i = pdf->xTics->data[i + 1] - pdf->xTics->data[i];
99  prob[i] = pdf->probDens->data[i] * dx_i;
100  }
101 
102  if ( ( pdf->sampling = gsl_ran_discrete_preproc( numBins, prob ) ) == NULL ) {
103  XLALPrintError( "%s: gsl_ran_discrete_preproc() failed\n", __func__ );
105  }
106 
107  XLALFree( prob );
108  } /* if pdf->sampling == NULL */
109 
110  // ----- draw an index from pdf->probDens
111  UINT4 ind = gsl_ran_discrete( rng, pdf->sampling );
112  // get the corresponding bin-boundaries of bin[i] = [x[i], x[i+1]]
113  REAL8 x0 = pdf->xTics->data[ind];
114  REAL8 x1 = pdf->xTics->data[ind + 1];
115 
116  // and do another uniform draw from [x0, x1] (thanks to Karl for that suggestion ;)
117  // this could possibly be further smoothed by drawing from a linear pdf interpolating the neighbouring bins..
118  return gsl_ran_flat( rng, x0, x1 );
119 
120 } /* XLALDrawFromPDF1D() */
121 
122 /**
123  * Checks internal consistency of pdf1D object.
124  *
125  * If lalDebugLevel > 0, also checks normalization of pdf if it claims to be normalized.
126  *
127  * Return: XLAL_SUCCESS if pdf seems OK, XLAL-error otherwise
128  */
129 int
130 XLALCheckValidPDF1D( const pdf1D_t *pdf )
131 {
132  /* check input consistency */
133  if ( !pdf ) {
134  XLALPrintError( "%s: NULL input 'pdf'\n", __func__ );
136  }
137 
138  /* check presence of required xTics array */
139  if ( ( pdf->xTics == NULL ) || ( pdf->xTics->length == 0 ) || ( pdf->xTics->data == NULL ) ) {
140  XLALPrintError( "%s: invalid pdf->xTics = NULL, length 0 or NULL data field \n", __func__ );
142  }
143 
144  /* ----- allowed special case 1: single value with certainty */
145  if ( ( pdf->xTics->length == 1 ) && ( pdf->probDens == NULL ) ) {
146  return XLAL_SUCCESS;
147  }
148 
149  /* ----- allowed special case 2: uniform pdf in [xMin, xMax] */
150  if ( ( pdf->xTics->length == 2 ) && ( pdf->probDens == NULL ) ) {
151  return XLAL_SUCCESS;
152  }
153 
154  /* ----- general case: discretized pdf ----- */
155  UINT4 numBins = pdf->xTics->length - 1;
156 
157  /* check valid pdf array */
158  if ( pdf->probDens == NULL ) {
159  XLALPrintError( "%s: invalid NULL pdf->probDens for required numBins=%d\n", __func__, numBins );
161  }
162  if ( pdf->probDens->length != numBins ) {
163  XLALPrintError( "%s: invalid length pdf->probDens->length=%d but should be %d\n", __func__, pdf->probDens->length, numBins );
165  }
166  if ( pdf->probDens->data == NULL ) {
167  XLALPrintError( "%s: invalid pdf->probDens->data = NULL\n", __func__ );
169  }
170 
171  /* ----- if pdf claims to be normalized, check that ----- */
172  if ( ( lalDebugLevel > 0 ) && pdf->isNormalized ) {
173  REAL8 norm = 0;
174  UINT4 i;
175  for ( i = 0; i < numBins; i ++ ) {
176  REAL8 dx_i = pdf->xTics->data[i + 1] - pdf->xTics->data[i];
177  norm += pdf->probDens->data[i] * dx_i; // sum up probability in bin[i]: prob[i] = pdf[i] * dx[i]
178  } /* for i < numBins */
179  REAL8 relErr = 1e-12; // generous, given double precision
180  if ( gsl_fcmp( norm, 1.0, relErr ) != 0 ) {
181  XLALPrintError( "%s: pdf claims to be normalized, but norm = %.16f differs from 1.0 by more than %g relative error\n", __func__, norm, relErr );
183  }
184  } // if pdf->isNormalized
185 
186  /* we found no problem, so we assume it's OK */
187  return XLAL_SUCCESS;
188 
189 } /* XLALCheckValidPDF1D() */
190 
191 /**
192  * Destructor function for 1-D pdf
193  */
194 void
195 XLALDestroyPDF1D( pdf1D_t *pdf )
196 {
197  if ( !pdf ) {
198  return;
199  }
200 
201  if ( pdf->xTics ) {
202  XLALDestroyREAL8Vector( pdf->xTics );
203  }
204  if ( pdf->probDens ) {
205  XLALDestroyREAL8Vector( pdf->probDens );
206  }
207 
208  if ( pdf->sampling ) {
209  gsl_ran_discrete_free( pdf->sampling );
210  }
211 
212 
213  XLALFree( pdf );
214 
215  return;
216 
217 } /* XLALDestroyPDF1D() */
218 
219 
220 /**
221  * Creator function for a 'singular' 1D pdf, containing a single value with certainty, ie P(x0)=1, and P(x!=x0)=0
222  *
223  * This is encoded as an xTics array containing just one value: x0, and prob=NULL, sampling=NULL
224  */
225 pdf1D_t *
226 XLALCreateSingularPDF1D( REAL8 x0 /**< domain of pdf is a single point: x0 */
227  )
228 {
229  /* allocate memory for output pdf */
230  pdf1D_t *ret;
231 
232  if ( ( ret = XLALCalloc( 1, sizeof( *ret ) ) ) == NULL ) {
233  XLALPrintError( "%s: failed to XLALCalloc ( 1, %zu)\n", __func__, sizeof( *ret ) );
235  }
236 
237  if ( ( ret->xTics = XLALCreateREAL8Vector( 1 ) ) == NULL ) {
238  XLALPrintError( "%s: surprisingly, XLALCreateREAL8Vector(1) failed!\n", __func__ );
239  XLALFree( ret );
241  }
242 
243  ret->xTics->data[0] = x0; /* only value required: P[x0]=1 */
244 
245  return ret;
246 
247 } /* XLALCreateSingularPDF1D() */
248 
249 /**
250  * Creator function for a uniform 1D pdf over [xMin, xMax]
251  *
252  * This is encoded as an xTics array containing just two values: x[0]=xMin, x[1]=xMax,
253  * and prob=NULL, sampling=NULL {not required to draw from this pdf}
254  */
255 pdf1D_t *
256 XLALCreateUniformPDF1D( REAL8 xMin, /**< lower boundary of domain interval */
257  REAL8 xMax /**< upper boundary of domain interval */
258  )
259 {
260  /* check input */
261  if ( xMax < xMin ) {
262  XLALPrintError( "%s: invalid input, xMax=%f must be > xMin = %f\n", __func__, xMax, xMin );
264  }
265 
266  /* allocate memory for output pdf */
267  pdf1D_t *ret;
268 
269  if ( ( ret = XLALCalloc( 1, sizeof( *ret ) ) ) == NULL ) {
270  XLALPrintError( "%s: failed to XLALCalloc ( 1, %zu)\n", __func__, sizeof( *ret ) );
272  }
273 
274  if ( ( ret->xTics = XLALCreateREAL8Vector( 2 ) ) == NULL ) {
275  XLALPrintError( "%s: surprisingly, XLALCreateREAL8Vector(2) failed!\n", __func__ );
276  XLALFree( ret );
278  }
279 
280  ret->xTics->data[0] = xMin;
281  ret->xTics->data[1] = xMax;
282 
283  return ret;
284 
285 } /* XLALCreateUniformPDF1D() */
286 
287 
288 /**
289  * Creator function for a generic discrete 1D pdf over [xMin, xMax], discretized into numBins bins
290  *
291  * NOTE: generates a uniform sampling of the domain [xMin, xMax] in numBins
292  * NOTE2: returns the P[i] array 'prob' initialized to 0, so after calling this function
293  * the user still needs to feed in the correct values for the probabilities P[i] of x in [x[i],x[i+1]]
294  */
295 pdf1D_t *
296 XLALCreateDiscretePDF1D( REAL8 xMin, /**< lower boundary of domain interval */
297  REAL8 xMax, /**< upper boundary of domain interval */
298  UINT4 numBins /**< number of bins to discretize PDF into */
299  )
300 {
301  /* check input */
302  if ( xMax < xMin ) {
303  XLALPrintError( "%s: invalid input, xMax=%f must be > xMin = %f\n", __func__, xMax, xMin );
305  }
306  if ( numBins == 0 ) {
307  XLALPrintError( "%s: invalid input, numBins must be positive!\n", __func__ );
309  }
310 
311  /* allocate memory for output pdf */
312  pdf1D_t *ret;
313 
314  if ( ( ret = XLALCalloc( 1, sizeof( *ret ) ) ) == NULL ) {
315  XLALPrintError( "%s: failed to XLALCalloc ( 1, %zu)\n", __func__, sizeof( *ret ) );
317  }
318 
319  if ( ( ret->xTics = XLALCreateREAL8Vector( numBins + 1 ) ) == NULL ) {
320  XLALPrintError( "%s: surprisingly, XLALCreateREAL8Vector(%d) failed!\n", __func__, numBins + 1 );
321  XLALFree( ret );
323  }
324  if ( ( ret->probDens = XLALCreateREAL8Vector( numBins ) ) == NULL ) {
325  XLALPrintError( "%s: surprisingly, XLALCreateREAL8Vector(%d) failed!\n", __func__, numBins );
326  XLALDestroyREAL8Vector( ret->xTics );
327  XLALFree( ret );
329  }
330 
331  /* initialize N bins uniformly spaced over [xMin, xMax], ie. N+1 'tics' */
332  UINT4 i;
333  REAL8 dx = ( xMax - xMin ) / numBins;
334  for ( i = 0; i < numBins + 1; i ++ ) {
335  REAL8 xi = xMin + i * dx;
336  ret->xTics->data[i] = xi;
337 
338  } /* for i < numBins+1 */
339 
340  /* initialized pdf bins to zero */
341  memset( ret->probDens->data, 0, numBins * sizeof( *ret->probDens->data ) );
342 
343  return ret;
344 
345 } /* XLALCreateDiscretePDF1D() */
346 
347 /**
348  * Method to normalize the given pdf1D.
349  * Only does something if necessary, ie if pdf isn't normalized already
350  */
351 int
352 XLALNormalizePDF1D( pdf1D_t *pdf )
353 {
354  /* sanity checks */
355  if ( !pdf ) {
356  XLALPrintError( "%s: invalid NULL input 'pdf'\n", __func__ );
358  }
359  if ( XLALCheckValidPDF1D( pdf ) != XLAL_SUCCESS ) {
360  XLALPrintError( "%s: invalid pdf\n", __func__ );
362  }
363 
364  /* ----- special case 1: single value with certainty: nothing to be done */
365  if ( ( pdf->xTics->length == 1 ) && ( pdf->probDens == NULL ) ) {
366  return XLAL_SUCCESS;
367  }
368 
369  /* ----- allowed special case 2: uniform pdf in [xMin, xMax] */
370  if ( ( pdf->xTics->length == 2 ) && ( pdf->probDens == NULL ) ) {
371  return XLAL_SUCCESS;
372  }
373 
374  /* is normalized already? nothing to be done */
375  if ( pdf->isNormalized ) {
376  return XLAL_SUCCESS;
377  }
378 
379  /* ----- general case: compute norm = int pdf(x) dx and divide pdf(x) to properly normalize */
380  UINT4 numBins = pdf->xTics->length - 1;
381  UINT4 i;
382  REAL8 norm = 0;
383  for ( i = 0; i < numBins; i ++ ) {
384  REAL8 dx_i = pdf->xTics->data[i + 1] - pdf->xTics->data[i];
385  norm += pdf->probDens->data[i] * dx_i; // sum up probability in bin[i]: prob[i] = pdf[i] * dx[i]
386  } /* for i < numBins */
387 
388  REAL8 invNorm = 1.0 / norm;
389  for ( i = 0; i < numBins; i ++ ) {
390  pdf->probDens->data[i] *= invNorm;
391  }
392 
393  pdf->isNormalized = 1;
394 
395  return XLAL_SUCCESS;
396 
397 } /* XLALNormalizePDF1D() */
398 
399 /**
400  * Function to write a pdf1D to given filepointer fp.
401  *
402  * Writes the pdf in octave format as a 2xN matrix, containing
403  * values { xtics[i], pdf[i] }
404  */
405 int
406 XLALOutputPDF1D_to_fp( FILE *fp, /**< output file-pointer to write into [append] */
407  const pdf1D_t *pdf, /**< input pdf [need not be normalized] */
408  const char *name /**< octave variable-name to use in output (default = 'pdf' if NULL) */
409  )
410 {
411  /* input sanity check */
412  if ( !fp || !pdf ) {
413  XLALPrintError( "%s: invalid NULL input 'fp=%p' or 'pdf=%p'\n", __func__, fp, pdf );
415  }
416  if ( XLALCheckValidPDF1D( pdf ) != XLAL_SUCCESS ) {
417  XLALPrintError( "%s: invalid probability-density 'pdf'\n", __func__ );
419  }
420 #define PDF_FMT "%.16g"
421 
422  const char *varname;
423  if ( name ) {
424  varname = name;
425  } else {
426  varname = "pdf";
427  }
428 
429  fprintf( fp, "%%%% Probability density function in octave format, as a 2xN matrix, containing value-pairs { x[i], pdf(x[i]) }\n" );
430  fprintf( fp, "%s = [ \\\n", varname );
431 
432  /* ----- special case 1: single value with certainty */
433  if ( ( pdf->xTics->length == 1 ) && ( pdf->probDens == NULL ) ) {
434  fprintf( fp, PDF_FMT ";\n" PDF_FMT ";\n];\n", pdf->xTics->data[0], 1.0 ); // not quite correct, should be a Dirac-delta function
435  return XLAL_SUCCESS;
436  }
437 
438  /* ----- special case 2: uniform pdf in [xMin, xMax] */
439  if ( ( pdf->xTics->length == 2 ) && ( pdf->probDens == NULL ) ) {
440  REAL8 x0 = pdf->xTics->data[0];
441  REAL8 x1 = pdf->xTics->data[1];
442  REAL8 p = 1.0 / ( x1 - x0 );
443  fprintf( fp, PDF_FMT ", " PDF_FMT ";\n" PDF_FMT ", " PDF_FMT ";\n];\n", x0, x1, p, p );
444  return XLAL_SUCCESS;
445  }
446 
447  /* ----- general case: discretized pdf ----- */
448  /* Note: Given that we only know the pdf-values over N finite-sized 'bins',
449  * we output N + 2 points for the pdf, namely we include the boundary-tics
450  * to fully represent the domain of the pdf.
451  * We use the values of the first and last 'bin' respectively for the
452  * boundary-values of the pdf, while using the N bin-centers for all the other values
453  * i.e. pdf = [
454  * x[0], xMid[0], xMid[1], xMid[2], ... xMid[N-1], x[N-1] ;
455  * p[0], p[0], p[1], p[2], .... p[N-1], p[N-1] ;
456  * ];
457  *
458  */
459  UINT4 numBins = pdf->xTics->length - 1;
460  UINT4 i;
461 
462  /* output the x-values x[i] */
463  fprintf( fp, PDF_FMT, pdf->xTics->data[0] );
464  for ( i = 0; i < numBins; i ++ ) {
465  REAL8 xMid = 0.5 * ( pdf->xTics->data[i] + pdf->xTics->data[i + 1] );
466  fprintf( fp, ", " PDF_FMT, xMid );
467  }
468  fprintf( fp, ", " PDF_FMT ";\n", pdf->xTics->data[numBins] );
469 
470  /* output the pdf values pdf(x[i]) */
471  fprintf( fp, PDF_FMT, pdf->probDens->data[0] );
472  for ( i = 0; i < numBins; i ++ ) {
473  fprintf( fp, ", " PDF_FMT, pdf->probDens->data[i] );
474  }
475  fprintf( fp, ", " PDF_FMT ";\n", pdf->probDens->data[numBins - 1] );
476 
477  fprintf( fp, " ];\n " );
478 
479 #undef PDF_FMT
480 
481  return XLAL_SUCCESS;
482 
483 } /* XLALOutputPDF1D_to_fp() */
484 
485 /**
486  * Find the 'mode' of the probabilty distribution, ie. the value x at which the pdf has its maximum.
487  *
488  * Note1: We return the center-value of the discrete 'bin' containing the maximum of the distribution 'pdf'.
489  * (in particular, for an explicitly uniform distribution (2 tics, probDens=NULL) we return the mid-point of the domain).
490  *
491  * Note2: If the mode is not unique, we return the *first* (ie 'leftmost') value of x.
492  *
493  */
494 REAL8
495 XLALFindModeOfPDF1D( const pdf1D_t *pdf )
496 {
497  /* check input */
498  if ( !pdf ) {
499  XLALPrintError( "%s: invalid NULL input 'pdf'\n", __func__ );
501  }
502  if ( XLALCheckValidPDF1D( pdf ) != XLAL_SUCCESS ) {
503  XLALPrintError( "%s: invalid input pdf\n", __func__ );
505  }
506 
507  /* special case 1) singular pdf */
508  if ( ( pdf->xTics->length == 1 ) && ( pdf->probDens == NULL ) ) {
509  return pdf->xTics->data[0]; // mode is trivially at the spike
510  }
511 
512  /* special case 2) uniform pdf in [xMin, xMax] ==> return center-value */
513  if ( ( pdf->xTics->length == 2 ) && ( pdf->probDens == NULL ) ) {
514  return 0.5 * ( pdf->xTics->data[0] + pdf->xTics->data[1] ); // center-point of 'bin'
515  }
516 
517  UINT4 numBins = pdf->probDens->length;
518  UINT4 i;
519  REAL8 max_p = 0;
520  UINT4 mode_i = 0;
521  for ( i = 0; i < numBins; i ++ ) {
522  REAL8 this_p = pdf->probDens->data[i];
523  if ( this_p > max_p ) { // strict monotony ==> first (ie leftmost) mode will be returned
524  max_p = this_p;
525  mode_i = i;
526  } /* if new maximum found */
527  } /* for i < numBins */
528 
529  REAL8 xmode = 0.5 * ( pdf->xTics->data[mode_i] + pdf->xTics->data[mode_i + 1] );
530 
531  return xmode;
532 
533 } /* XLALFindModeOfPDF1D() */
#define __func__
log an I/O error, i.e.
pdf1D_t * XLALCreateUniformPDF1D(REAL8 xMin, REAL8 xMax)
Creator function for a uniform 1D pdf over [xMin, xMax].
int XLALOutputPDF1D_to_fp(FILE *fp, const pdf1D_t *pdf, const char *name)
Function to write a pdf1D to given filepointer fp.
pdf1D_t * XLALCreateDiscretePDF1D(REAL8 xMin, REAL8 xMax, UINT4 numBins)
Creator function for a generic discrete 1D pdf over [xMin, xMax], discretized into numBins bins.
int XLALNormalizePDF1D(pdf1D_t *pdf)
Method to normalize the given pdf1D.
REAL8 XLALFindModeOfPDF1D(const pdf1D_t *pdf)
Find the 'mode' of the probabilty distribution, ie.
#define PDF_FMT
pdf1D_t * XLALCreateSingularPDF1D(REAL8 x0)
Creator function for a 'singular' 1D pdf, containing a single value with certainty,...
void XLALDestroyPDF1D(pdf1D_t *pdf)
Destructor function for 1-D pdf.
REAL8 XLALDrawFromPDF1D(pdf1D_t *pdf, const gsl_rng *rng)
Function to generate random samples drawn from the given pdf(x) NOTE: if the 'sampling' field is NULL...
int XLALCheckValidPDF1D(const pdf1D_t *pdf)
Checks internal consistency of pdf1D object.
const char * name
Definition: SearchTiming.c:93
#define fprintf
double e
double REAL8
uint32_t UINT4
#define lalDebugLevel
void * XLALCalloc(size_t m, size_t n)
void * XLALMalloc(size_t n)
void XLALFree(void *p)
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
#define XLAL_ERROR_REAL8(...)
#define XLAL_ERROR_NULL(...)
#define XLAL_ERROR(...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFUNC
XLAL_EDOM
XLAL_EINVAL
XLAL_EFAILED