ex-1: generalise numeric_mode to take any pdf

This commit is contained in:
Michele Guerini Rocco 2020-04-06 16:57:00 +02:00
parent ff56757258
commit dce8cfb8b6
5 changed files with 60 additions and 23 deletions

View File

@ -8,9 +8,9 @@
#include <gsl/gsl_integration.h> #include <gsl/gsl_integration.h>
/* This is a wrapper needed by `landau_cdf` because /* This is a wrapper needed by `landau_cdf` and
* the numerical integration expects a function * other optimisation functions because the GSL
* with parameters. * routines expect a function with parameters.
*/ */
double landau_pdf(double x, void* params) { double landau_pdf(double x, void* params) {
return gsl_ran_landau_pdf(x); return gsl_ran_landau_pdf(x);

View File

@ -5,9 +5,9 @@
#pragma once #pragma once
/* This is a wrapper needed by `landau_cdf` because /* This is a wrapper needed by `landau_cdf` and
* the numerical integration expects a function * other optimisation functions because the GSL
* with parameters. * routines expect a function with parameters.
*/ */
double landau_pdf(double x, void* params); double landau_pdf(double x, void* params);

View File

@ -77,9 +77,18 @@ int main(int argc, char** argv) {
fprintf(stderr, "\n\n# Mode comparison\n"); fprintf(stderr, "\n\n# Mode comparison\n");
// print the results /* A structure used by the optimisation
double mode_e = numeric_mode(min, max); * routines in numeric_mode and others
* functions below.
*/
gsl_function pdf;
pdf.function = &landau_pdf;
pdf.params = NULL;
double mode_e = numeric_mode(min, max, &pdf);
uncert mode_o = bootstrap_mode(r, sample, samples, 100); uncert mode_o = bootstrap_mode(r, sample, samples, 100);
// print the results
fprintf(stderr, "\n## Results\n"); fprintf(stderr, "\n## Results\n");
fprintf(stderr, "expected mode: %.7f\n", mode_e); fprintf(stderr, "expected mode: %.7f\n", mode_e);
fprintf(stderr, "observed mode: %.4f±%.4f\n", mode_o.n, mode_o.s); fprintf(stderr, "observed mode: %.4f±%.4f\n", mode_o.n, mode_o.s);

View File

@ -45,12 +45,29 @@ double kolmogorov_cdf(double D, int n) {
} }
/* This is a wrapper needed by `numeric_mode` because
* the minimization expects a function to be minimized and not /* This is a high-order function (ie a function that operates
* maximized. * on functions) in disguise. It takes a function f and produces
* a function that computes -f. In lambda calculus it would be
* the map λf.λx -f(x).
*
* Since there is no notion of lambda functions in C (the
* standard one, at least) we use a trick involving the
* gsl_function struct: `negate_func(x, fp)` takes a point `x`
* and a gsl_function `fp` as the usual void pointer `params`.
* It then calls the function `fp.function` with `x` and their
* `fp.params` and return the negated result.
*
* So, given a `gsl_function f` its negated function is
* contructed as follows:
*
* gsl_function nf;
* nf.function = &negate_func;
* nf.params = &f;
*/ */
double neg_landau_pdf(double x, void* params) { double negate_func(double x, void * fp) {
return (-1) * gsl_ran_landau_pdf(x); gsl_function f = *((gsl_function*) fp);
return -f.function(x, f.params);
} }
@ -59,11 +76,16 @@ double neg_landau_pdf(double x, void* params) {
* The min,max parameters are the initial search * The min,max parameters are the initial search
* interval for the optimisation. * interval for the optimisation.
*/ */
double numeric_mode(double min, double max) { double numeric_mode(double min, double max,
// create function gsl_function *pdf) {
gsl_function pdf;
pdf.function = &neg_landau_pdf; /* Negate the PDF to maximise it by
pdf.params = NULL; * using a GSL minimisation method.
* (There usually are no maximisation methods)
*/
gsl_function npdf;
npdf.function = &negate_func;
npdf.params = pdf;
// initialize minimization // initialize minimization
double guess = 0; double guess = 0;
@ -71,11 +93,11 @@ double numeric_mode(double min, double max) {
int max_iter = 100; int max_iter = 100;
double prec = 1e-7; double prec = 1e-7;
int status; int status;
const gsl_min_fminimizer_type * T = gsl_min_fminimizer_goldensection; const gsl_min_fminimizer_type * T = gsl_min_fminimizer_brent;
gsl_min_fminimizer * s = gsl_min_fminimizer_alloc(T); gsl_min_fminimizer * s = gsl_min_fminimizer_alloc(T);
gsl_min_fminimizer_set(s, &pdf, guess, min, max); gsl_min_fminimizer_set(s, &npdf, guess, min, max);
// minimization // minimisation
do { do {
iter++; iter++;
status = gsl_min_fminimizer_iterate(s); status = gsl_min_fminimizer_iterate(s);
@ -86,9 +108,12 @@ double numeric_mode(double min, double max) {
} while (status == GSL_CONTINUE && iter < max_iter); } while (status == GSL_CONTINUE && iter < max_iter);
/* The error is simply given by the width of
* the final interval containing the solution
*/
fprintf(stderr, "mode error: %.3g\n", max - min); fprintf(stderr, "mode error: %.3g\n", max - min);
// Free memory // free memory
gsl_min_fminimizer_free(s); gsl_min_fminimizer_free(s);
return guess; return guess;
} }

View File

@ -3,6 +3,8 @@
* from a Landau distribution. * from a Landau distribution.
*/ */
#include <gsl/gsl_roots.h>
#pragma once #pragma once
/* Kolmogorov distribution CDF /* Kolmogorov distribution CDF
@ -16,7 +18,8 @@ double kolmogorov_cdf(double D, int n);
* The min,max parameters are the initial search * The min,max parameters are the initial search
* interval for the optimisation. * interval for the optimisation.
*/ */
double numeric_mode(double min, double max); double numeric_mode(double min, double max,
gsl_function *pdf);
/* Numerically computes the FWHM of Landau /* Numerically computes the FWHM of Landau