Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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 */
59XLALDrawFromPDF1D( 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 */
129int
130XLALCheckValidPDF1D( 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 */
194void
195XLALDestroyPDF1D( 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 */
225pdf1D_t *
226XLALCreateSingularPDF1D( 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 */
255pdf1D_t *
256XLALCreateUniformPDF1D( 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 */
295pdf1D_t *
296XLALCreateDiscretePDF1D( 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 */
351int
352XLALNormalizePDF1D( 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 */
405int
406XLALOutputPDF1D_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 */
494REAL8
495XLALFindModeOfPDF1D( 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.
int XLALOutputPDF1D_to_fp(FILE *fp, const pdf1D_t *pdf, const char *name)
Function to write a pdf1D to given filepointer fp.
pdf1D_t * XLALCreateSingularPDF1D(REAL8 x0)
Creator function for a 'singular' 1D pdf, containing a single value with certainty,...
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.
pdf1D_t * XLALCreateUniformPDF1D(REAL8 xMin, REAL8 xMax)
Creator function for a uniform 1D pdf over [xMin, xMax].
#define PDF_FMT
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 * XLALMalloc(size_t n)
void * XLALCalloc(size_t m, 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