/* This file contains functions to perform * statistical tests on the points sampled * from a Landau distribution. */ #include #include #include #include #include #include "landau.h" /* Kolmogorov distribution CDF * for sample size n and statistic D */ double kolmogorov_cdf(double D, int n) { double x = sqrt(n) * D; // trick to reduce estimate error x += 1/(6 * sqrt(n)) + (x - 1)/(4 * n); // calculate the first n_terms of the series // Σ_k=1 exp(-(2k - 1)²π²/8x²) size_t n_terms = 30; double *terms = calloc(n_terms, sizeof(double)); for (size_t k=0; ksum_plain); fprintf(stderr, "err: %g\n", abserr); gsl_sum_levin_utrunc_free(s); free(terms); return sqrt(2*M_PI)/x * sum; } /* 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 negate_func(double x, void * fp) { gsl_function f = *((gsl_function*) fp); return -f.function(x, f.params); } /* Numerically computes the mode of a Landau * distribution by maximising the derivative. * The `min,max` parameters are the initial search * interval for the optimisation. * * If `err` is true print the estimate error. */ double numeric_mode(double min, double max, gsl_function *pdf, int err) { /* 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 x = 0; int max_iter = 100; double prec = 1e-7; int status; const gsl_min_fminimizer_type *T = gsl_min_fminimizer_brent; gsl_min_fminimizer *s = gsl_min_fminimizer_alloc(T); gsl_min_fminimizer_set(s, &npdf, x, min, max); // minimisation for (int iter = 0; status == GSL_CONTINUE && iter < max_iter; iter++) { status = gsl_min_fminimizer_iterate(s); x = gsl_min_fminimizer_x_minimum(s); min = gsl_min_fminimizer_x_lower(s); max = gsl_min_fminimizer_x_upper(s); status = gsl_min_test_interval(min, max, 0, prec); } /* The error is simply given by the width of * the final interval containing the solution */ if (err) fprintf(stderr, "mode error: %.3g\n", max - min); // free memory gsl_min_fminimizer_free(s); return x; } /* A structure containing the half-maximum * of a PDF and a gsl_function of the PDF * itself, used by `numeric_fwhm` to compute * the FWHM. */ struct fwhm_params { double halfmax; gsl_function *pdf; }; /* This is the implicit equation that is solved * in `numeric_fwhm` by a numerical root-finding * method. This function takes a point `x`, the * parameters defined in `fwhm_params` and returns * the value of: * * f(x) - f(max)/2 * * where f is the PDF of interest. */ double fwhm_equation(double x, void* params) { struct fwhm_params p = *((struct fwhm_params*) params); return p.pdf->function(x, p.pdf->params) - p.halfmax; } /* Numerically computes the FWHM of a PDF * distribution using the definition. * The `min,max` parameters are the initial search * interval for the root search of equation * `fwhm_equation`. Two searches are performed in * [min, mode] and [mode, max] that will give the * two solutions x₋, x₊. The FWHM is then x₊-x₋. * * If `err` is true print the estimate error. */ double numeric_fwhm(double min, double max, gsl_function *pdf, int err) { /* Create the gls_function structure * for the equation to be solved. */ double mode = numeric_mode(min, max, pdf, 0); struct fwhm_params p = { pdf->function(mode, pdf->params)/2, pdf }; gsl_function equation; equation.function = &fwhm_equation; equation.params = &p; const gsl_root_fsolver_type *T = gsl_root_fsolver_brent; gsl_root_fsolver *s = gsl_root_fsolver_alloc(T); // initialize minimization for x₋ double x, fmin, fmax; int max_iter = 100; double prec = 1e-7; int status; // minimization gsl_root_fsolver_set(s, &equation, min, mode); status = GSL_CONTINUE; for (int iter = 0; status == GSL_CONTINUE && iter < max_iter; iter++) { status = gsl_root_fsolver_iterate(s); x = gsl_root_fsolver_root(s); fmin = gsl_root_fsolver_x_lower(s); fmax = gsl_root_fsolver_x_upper(s); status = gsl_min_test_interval(fmin, fmax, 0, prec); } double x_low = x; double err_low = fmax - fmin; // initialize minimization for x₊ gsl_root_fsolver_set(s, &equation, mode, max); // minimization status = GSL_CONTINUE; for (int iter = 0; status == GSL_CONTINUE && iter < max_iter; iter++) { status = gsl_root_fsolver_iterate(s); x = gsl_root_fsolver_root(s); fmin = gsl_root_fsolver_x_lower(s); fmax = gsl_root_fsolver_x_upper(s); status = gsl_min_test_interval(fmin, fmax, 0, prec); } double x_upp = x; double err_upp = fmax - fmin; if (err) fprintf(stderr, "fhwm error: %g\n", sqrt(err_low*err_low + err_upp*err_upp)); // free memory gsl_root_fsolver_free(s); return x_upp - x_low; }