222 lines
6.1 KiB
C
222 lines
6.1 KiB
C
/* This file contains functions to perform
|
|
* statistical tests on the points sampled
|
|
* from a Landau distribution.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <gsl/gsl_randist.h>
|
|
#include <gsl/gsl_min.h>
|
|
#include <gsl/gsl_roots.h>
|
|
#include <gsl/gsl_sum.h>
|
|
|
|
#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; k<n_terms; k++) {
|
|
terms[k] = exp(-pow((2*(double)(k + 1) - 1)*M_PI/x, 2) / 8);
|
|
}
|
|
|
|
// do a transform to accelerate the convergence
|
|
double sum, abserr;
|
|
gsl_sum_levin_utrunc_workspace* s = gsl_sum_levin_utrunc_alloc(n_terms);
|
|
gsl_sum_levin_utrunc_accel(terms, n_terms, s, &sum, &abserr);
|
|
|
|
fprintf(stderr, "\n## Kolmogorov CDF\n");
|
|
fprintf(stderr, "accel sum: %g\n", sum);
|
|
fprintf(stderr, "plain sum: %g\n", s->sum_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;
|
|
}
|