From dce8cfb8b64a95b89a86691e39eda42479b8bf09 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Mon, 6 Apr 2020 16:57:00 +0200 Subject: [PATCH] ex-1: generalise numeric_mode to take any pdf --- ex-1/landau.c | 6 +++--- ex-1/landau.h | 6 +++--- ex-1/main.c | 13 +++++++++++-- ex-1/tests.c | 53 +++++++++++++++++++++++++++++++++++++-------------- ex-1/tests.h | 5 ++++- 5 files changed, 60 insertions(+), 23 deletions(-) diff --git a/ex-1/landau.c b/ex-1/landau.c index 6a4a982..bef2c46 100644 --- a/ex-1/landau.c +++ b/ex-1/landau.c @@ -8,9 +8,9 @@ #include -/* This is a wrapper needed by `landau_cdf` because - * the numerical integration expects a function - * with parameters. +/* This is a wrapper needed by `landau_cdf` and + * other optimisation functions because the GSL + * routines expect a function with parameters. */ double landau_pdf(double x, void* params) { return gsl_ran_landau_pdf(x); diff --git a/ex-1/landau.h b/ex-1/landau.h index 19b5a5e..c13a62c 100644 --- a/ex-1/landau.h +++ b/ex-1/landau.h @@ -5,9 +5,9 @@ #pragma once -/* This is a wrapper needed by `landau_cdf` because - * the numerical integration expects a function - * with parameters. +/* This is a wrapper needed by `landau_cdf` and + * other optimisation functions because the GSL + * routines expect a function with parameters. */ double landau_pdf(double x, void* params); diff --git a/ex-1/main.c b/ex-1/main.c index 45c58b6..3e204ea 100644 --- a/ex-1/main.c +++ b/ex-1/main.c @@ -77,9 +77,18 @@ int main(int argc, char** argv) { fprintf(stderr, "\n\n# Mode comparison\n"); - // print the results - double mode_e = numeric_mode(min, max); + /* A structure used by the optimisation + * 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); + + // print the results fprintf(stderr, "\n## Results\n"); fprintf(stderr, "expected mode: %.7f\n", mode_e); fprintf(stderr, "observed mode: %.4f±%.4f\n", mode_o.n, mode_o.s); diff --git a/ex-1/tests.c b/ex-1/tests.c index b2e85f8..d2cf017 100644 --- a/ex-1/tests.c +++ b/ex-1/tests.c @@ -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 - * maximized. + +/* This is a high-order function (ie a function that operates + * 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) { - return (-1) * gsl_ran_landau_pdf(x); +double negate_func(double x, void * fp) { + 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 * interval for the optimisation. */ -double numeric_mode(double min, double max) { - // create function - gsl_function pdf; - pdf.function = &neg_landau_pdf; - pdf.params = NULL; +double numeric_mode(double min, double max, + gsl_function *pdf) { + + /* Negate the PDF to maximise it by + * using a GSL minimisation method. + * (There usually are no maximisation methods) + */ + gsl_function npdf; + npdf.function = &negate_func; + npdf.params = pdf; // initialize minimization double guess = 0; @@ -71,11 +93,11 @@ double numeric_mode(double min, double max) { int max_iter = 100; double prec = 1e-7; 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_set(s, &pdf, guess, min, max); + gsl_min_fminimizer_set(s, &npdf, guess, min, max); - // minimization + // minimisation do { iter++; status = gsl_min_fminimizer_iterate(s); @@ -86,9 +108,12 @@ double numeric_mode(double min, double max) { } 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); - // Free memory + // free memory gsl_min_fminimizer_free(s); return guess; } diff --git a/ex-1/tests.h b/ex-1/tests.h index 17aed2f..f8f1f6b 100644 --- a/ex-1/tests.h +++ b/ex-1/tests.h @@ -3,6 +3,8 @@ * from a Landau distribution. */ +#include + #pragma once /* Kolmogorov distribution CDF @@ -16,7 +18,8 @@ double kolmogorov_cdf(double D, int n); * The min,max parameters are the initial search * 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