analistica/ex-6/test.c

224 lines
7.6 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <math.h>
#include <string.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_statistics_double.h>
#include "common.h"
#include "fft.h"
#include "rl.h"
#include "dist.h"
/* Program options */
struct options {
size_t num_events;
size_t num_exps;
size_t bins;
double sigma;
size_t rounds;
const char* mode;
double noise;
};
int show_help(char **argv) {
fprintf(stderr, "Usage: %s -[hexbsrn]\n", argv[0]);
fprintf(stderr, " -h\t\tShow this message.\n");
fprintf(stderr, " -e N\t\tThe number of events. (default: 50000)\n");
fprintf(stderr, " -x N\t\tThe number of experiments to run. (default: 1000)\n");
fprintf(stderr, " -b N\t\tThe number of θ bins. (default: 150)\n");
fprintf(stderr, " -s SIGMA\tThe sigma of gaussian kernel. "
"(default: 0.8)\n");
fprintf(stderr, " -r N\t\tThe number of RL deconvolution rounds."
"(default: 3)\n");
fprintf(stderr, " -n SIGMA\tThe σ of the gaussian noise to add to "
"the convolution. (default: 0)\n");
return EXIT_SUCCESS;
}
/* Performs an experiment consisting in
*
* 1. Measuring the distribution I(θ) sampling from
* an RNG;
* 2. Convolving the I(θ) sample with a kernel
* to simulate the instrumentation response;
* 3. Applying a gaussian noise with σ=opts.noise
* 4. Deconvolving the result with both the
* Richardson-Lucy (RL) algorithm and the Fourier
* transform (FFT) method;
* 5. Computing the Earth Mover's Distance (EMD) between
* the deconvolution results and the original,
* uncorrupted sample.
*
* The function returns a pair of the FFT and RL
* distances, in this order.
*/
double *experiment(
struct options opts,
gsl_rng *r,
gsl_histogram *kernel) {
struct param p = { 0.01, 0.0001, 1e4, 1 };
/* Sample events following the intensity
* of a circular aperture diffraction I(θ)
* while producing a histogram.
*/
gsl_histogram* hist = gsl_histogram_alloc(opts.bins);
gsl_histogram_set_ranges_uniform(hist, 0, M_PI/2);
for (size_t i = 0; i < opts.num_events; i++){
double theta;
do {
// uniform sphere polar angle
theta = acos(1 - gsl_rng_uniform(r));
} while(intensity(0, p) * gsl_rng_uniform(r) > intensity(theta, p));
gsl_histogram_increment(hist, theta);
}
/* Convolve the hisogram with the kernel */
gsl_histogram *conv = histogram_convolve(hist, kernel);
/* Add gaussian noise with σ=opts.noise */
if (opts.noise > 0) {
for (size_t i = 0; i < conv->n; i++)
conv->bin[i] += conv->bin[i] * gsl_ran_gaussian(r, opts.noise);
}
/* Deconvolve the histogram with both methods: RL and FFT.
* The FFT is used as reference for the distance.
*/
gsl_histogram *fft_clean = fft_deconvolve(conv, kernel);
gsl_histogram *rl_clean = rl_deconvolve(conv, kernel, opts.rounds);
/* Compute the earth mover's distances from the original
* histogram and store add each one to the respective
* distance histogram.
*/
static double dist[3];
dist[0] = emd_between(hist, fft_clean);
dist[1] = emd_between(hist, rl_clean);
dist[2] = emd_between(hist, conv);
// free memory
gsl_histogram_free(hist);
gsl_histogram_free(conv);
gsl_histogram_free(fft_clean);
gsl_histogram_free(rl_clean);
return dist;
}
int main(int argc, char **argv) {
struct options opts;
/* Set default options */
opts.num_events = 50000;
opts.num_exps = 1000;
opts.bins = 150;
opts.sigma = 0.8;
opts.rounds = 3;
opts.mode = "fft";
opts.noise = 0;
/* Process CLI arguments */
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-e")) opts.num_events = atol(argv[++i]);
else if (!strcmp(argv[i], "-x")) opts.num_exps = atol(argv[++i]);
else if (!strcmp(argv[i], "-b")) opts.bins = atol(argv[++i]);
else if (!strcmp(argv[i], "-s")) opts.sigma = atof(argv[++i]);
else if (!strcmp(argv[i], "-r")) opts.rounds = atol(argv[++i]);
else if (!strcmp(argv[i], "-n")) opts.noise = atof(argv[++i]);
else return show_help(argv);
}
/* Initialize an RNG. */
gsl_rng_env_setup();
gsl_rng *r = gsl_rng_alloc(gsl_rng_default);
/* Generate the gaussian kernel */
gsl_histogram *kernel = gaussian_kernel(opts.bins, opts.sigma);
/* Performs experiments and record the deconvolution
* EMD distances to the original sample.
*/
double* fft_dist = calloc(opts.num_exps, sizeof(double));
double* rl_dist = calloc(opts.num_exps, sizeof(double));
double* conv_dist = calloc(opts.num_exps, sizeof(double));
for (size_t i = 0; i < opts.num_exps; i++) {
double *dist = experiment(opts, r, kernel);
fft_dist[i] = dist[0];
rl_dist[i] = dist[1];
conv_dist[i] = dist[2];
}
/* Compute some statistics of the EMD */
// 1 is the array stride.
double fft_mean = gsl_stats_mean(fft_dist, 1, opts.num_exps);
double fft_stdev = gsl_stats_sd(fft_dist, 1, opts.num_exps);
double fft_skew = gsl_stats_skew_m_sd(fft_dist, 1, opts.num_exps, fft_mean, fft_stdev);
double fft_min, fft_max; gsl_stats_minmax(&fft_min, &fft_max, fft_dist, 1, opts.num_exps);
double rl_mean = gsl_stats_mean(rl_dist, 1, opts.num_exps);
double rl_stdev = gsl_stats_sd(rl_dist, 1, opts.num_exps);
double rl_skew = gsl_stats_skew_m_sd(rl_dist, 1, opts.num_exps, rl_mean, rl_stdev);
double rl_min, rl_max; gsl_stats_minmax(&rl_min, &rl_max, rl_dist, 1, opts.num_exps);
double conv_mean = gsl_stats_mean(conv_dist, 1, opts.num_exps);
double conv_stdev = gsl_stats_sd(conv_dist, 1, opts.num_exps);
double conv_skew = gsl_stats_skew_m_sd(conv_dist, 1, opts.num_exps, conv_mean, conv_stdev);
double conv_min, conv_max; gsl_stats_minmax(&conv_min, &conv_max, conv_dist, 1, opts.num_exps);
/* Create EDM distance histograms.
* Since the distance depends wildly on the noise we can't
* set a fixed range and therefore use the above values.
*/
gsl_histogram *fft_hist = gsl_histogram_alloc(sqrt(opts.num_exps));
gsl_histogram *rl_hist = gsl_histogram_alloc(sqrt(opts.num_exps));
gsl_histogram *conv_hist = gsl_histogram_alloc(sqrt(opts.num_exps));
gsl_histogram_set_ranges_uniform( fft_hist, fft_min, fft_max);
gsl_histogram_set_ranges_uniform( rl_hist, rl_min, rl_max);
gsl_histogram_set_ranges_uniform(conv_hist, conv_min, conv_max);
for (size_t i = 0; i < opts.num_exps; i++) {
gsl_histogram_increment( fft_hist, fft_dist[i]);
gsl_histogram_increment( rl_hist, rl_dist[i]);
gsl_histogram_increment(conv_hist, conv_dist[i]);
}
// print results to stderr
fprintf(stderr, "# EMD distance\n\n");
fprintf(stderr, "## FFT deconvolution\n");
fprintf(stderr, "- mean: %.2e\n"
"- stdev: %.1e\n"
"- skew: %.2f\n", fft_mean, fft_stdev, fft_skew);
fprintf(stderr, "\n## RL deconvolution\n");
fprintf(stderr, "- mean: %.2e\n"
"- stdev: %.1e\n"
"- skew: %.2f\n", rl_mean, rl_stdev, rl_skew);
fprintf(stderr, "\n## convolution\n");
fprintf(stderr, "- mean: %.2e\n"
"- stdev: %.1e\n"
"- skew: %.2f\n", conv_mean, conv_stdev, conv_skew);
// print histograms to stdout
fprintf(stderr, "\n# EMD histogram\n");
fprintf(stdout, "%d\n", (int)sqrt(opts.num_exps));
gsl_histogram_fprintf(stdout, fft_hist, "%.10g", "%.10g");
gsl_histogram_fprintf(stdout, rl_hist, "%.10g", "%.10g");
gsl_histogram_fprintf(stdout, conv_hist, "%.10g", "%.10g");
// free memory
gsl_rng_free(r);
gsl_histogram_free(kernel);
gsl_histogram_free( fft_hist);
gsl_histogram_free( rl_hist);
gsl_histogram_free(conv_hist);
free( fft_dist);
free( rl_dist);
free(conv_dist);
return EXIT_SUCCESS;
}