ex-6: initial work on deconvolution testing
This commit is contained in:
parent
19981225f2
commit
b865dd83ac
138
ex-6/common.c
Normal file
138
ex-6/common.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#include <math.h>
|
||||||
|
#include <gsl/gsl_sf.h>
|
||||||
|
#include <gsl/gsl_randist.h>
|
||||||
|
#include <gsl/gsl_blas.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Intensity of the EM field at a large distance
|
||||||
|
* from a circular aperture diffraction of a plane
|
||||||
|
* wave.
|
||||||
|
*
|
||||||
|
* The intensity is given as a function of θ,
|
||||||
|
* the diffraction angle. All the other physical
|
||||||
|
* parameters are controlled by the structure `param`.
|
||||||
|
*
|
||||||
|
* See Fraunhöfer diffraction on "Hect - Optics" for
|
||||||
|
* a derivation of the formula.
|
||||||
|
*/
|
||||||
|
double intensity(double theta, struct param p) {
|
||||||
|
double x = p.k * p.a * sin(theta);
|
||||||
|
double R = p.l / cos(theta);
|
||||||
|
|
||||||
|
/* The intensity function has an eliminable
|
||||||
|
* discontinuoty at the origin, which must
|
||||||
|
* be handled separately.
|
||||||
|
*/
|
||||||
|
if (fabs(x) < 1e-15) {
|
||||||
|
return 0.5 * pow(p.e/R * M_PI * pow(p.a, 2), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
double y = 2*M_PI*pow(p.a, 2) * gsl_sf_bessel_J1(x) / x;
|
||||||
|
return 0.5 * pow(p.e * y/R, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* `gaussian_kernel(bins, sigma)` generates a convolution
|
||||||
|
* kernel for the intensity histogram with `bins` bins.
|
||||||
|
*
|
||||||
|
* The kernel is a gaussian PDF with μ at the central bin
|
||||||
|
* and σ equal to `sigma`. The size is 6% of `bins` and
|
||||||
|
* the bin width is the same of the intensity histogram:
|
||||||
|
* π/2 / `bins`.
|
||||||
|
*/
|
||||||
|
gsl_histogram* gaussian_kernel(size_t bins, double sigma) {
|
||||||
|
/* Set the size to 6% of the histogram.
|
||||||
|
* Always choose n even to correctly center the
|
||||||
|
* gaussian in the middle.
|
||||||
|
*/
|
||||||
|
size_t n = ((double) bins * 6) / 100;
|
||||||
|
n = n % 2 ? n : n+1;
|
||||||
|
|
||||||
|
double dx = (M_PI/2) / bins;
|
||||||
|
|
||||||
|
gsl_histogram *res = gsl_histogram_alloc(n);
|
||||||
|
gsl_histogram_set_ranges_uniform(res, 0, dx * n);
|
||||||
|
|
||||||
|
/* The histogram will be such that
|
||||||
|
* the maximum falls in the central bin.
|
||||||
|
*/
|
||||||
|
long int offset = (res->n)/2;
|
||||||
|
|
||||||
|
for (long int i = 0; i < (long int)res->n; i++)
|
||||||
|
res->bin[i] = gsl_ran_gaussian_pdf(
|
||||||
|
((double)(i - offset)) * dx, sigma * dx);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* `gsl_vector_convolve(a, b, res)` computes the linear
|
||||||
|
* convolution of two vectors. The resulting vector
|
||||||
|
* has size `a->n + b->n - 1` and is stored in `res`.
|
||||||
|
*/
|
||||||
|
int gsl_vector_convolve(gsl_vector *a, gsl_vector *b, gsl_vector *res) {
|
||||||
|
size_t m = a->size;
|
||||||
|
size_t n = b->size;
|
||||||
|
|
||||||
|
/* Vector views for the overlap dot product */
|
||||||
|
gsl_vector_view va, vb;
|
||||||
|
|
||||||
|
/* Reverse `b` before convolving */
|
||||||
|
gsl_vector_reverse(b);
|
||||||
|
|
||||||
|
double dot;
|
||||||
|
size_t start_a, start_b, many;
|
||||||
|
for (size_t i = 0; i < m + n - 1; i++) {
|
||||||
|
/* Calculate the starting indices
|
||||||
|
* of the two vectors a,b overlap.
|
||||||
|
*/
|
||||||
|
if (i < n) start_a = 0;
|
||||||
|
else start_a = i - (n - 1);
|
||||||
|
|
||||||
|
if (i < n) start_b = (n - 1) - i;
|
||||||
|
else start_b = 0;
|
||||||
|
|
||||||
|
/* Calculate how many elements overlap */
|
||||||
|
if (i < n) many = i + 1;
|
||||||
|
else if (i >= m) many = n - (i - m) - 1;
|
||||||
|
else many = n;
|
||||||
|
|
||||||
|
/* Take the dot product of the overlap
|
||||||
|
* and store it in the resulting histogram
|
||||||
|
*/
|
||||||
|
va = gsl_vector_subvector(a, start_a, many);
|
||||||
|
vb = gsl_vector_subvector(b, start_b, many);
|
||||||
|
gsl_blas_ddot(&va.vector, &vb.vector, &dot);
|
||||||
|
gsl_vector_set(res, i, dot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Put b back together */
|
||||||
|
gsl_vector_reverse(b);
|
||||||
|
|
||||||
|
return GSL_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Convolves two histograms while keeping the
|
||||||
|
* original bin edges. This is a simple wrapper
|
||||||
|
* function around `gsl_vector_convolve`.
|
||||||
|
*/
|
||||||
|
gsl_histogram* histogram_convolve(gsl_histogram *a, gsl_histogram *b) {
|
||||||
|
gsl_histogram *res = gsl_histogram_calloc(a->n + b->n - 1);
|
||||||
|
|
||||||
|
/* Set the same edges as `a`*/
|
||||||
|
double max = gsl_histogram_max(a);
|
||||||
|
double min = gsl_histogram_min(a);
|
||||||
|
gsl_histogram_set_ranges_uniform(res, min, max);
|
||||||
|
|
||||||
|
/* Create vector views for everybody */
|
||||||
|
gsl_vector_view va = gsl_vector_view_array(a->bin, a->n);
|
||||||
|
gsl_vector_view vb = gsl_vector_view_array(b->bin, b->n);
|
||||||
|
gsl_vector_view vres = gsl_vector_view_array(res->bin, res->n);
|
||||||
|
|
||||||
|
gsl_vector_convolve(&va.vector, &vb.vector, &vres.vector);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
51
ex-6/common.h
Normal file
51
ex-6/common.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gsl/gsl_histogram.h>
|
||||||
|
#include <gsl/gsl_vector_double.h>
|
||||||
|
|
||||||
|
/* Intensity function parameters */
|
||||||
|
struct param {
|
||||||
|
double a; // aperture radius
|
||||||
|
double k; // wavenumber
|
||||||
|
double e; // wave amplitude
|
||||||
|
double l; // screen distance
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Intensity of the EM field at a large distance
|
||||||
|
* from a circular aperture diffraction of a plane
|
||||||
|
* wave.
|
||||||
|
*
|
||||||
|
* The intensity is given as a function of θ,
|
||||||
|
* the diffraction angle. All the other physical
|
||||||
|
* parameters are controlled by the structure `param`.
|
||||||
|
*
|
||||||
|
* See Fraunhöfer diffraction on "Hect - Optics" for
|
||||||
|
* a derivation of the formula.
|
||||||
|
*/
|
||||||
|
double intensity(double theta, struct param p);
|
||||||
|
|
||||||
|
|
||||||
|
/* `gaussian_kernel(bins, sigma)` generates a convolution
|
||||||
|
* kernel for the intensity histogram with `bins` bins.
|
||||||
|
*
|
||||||
|
* The kernel is a gaussian PDF with μ at the central bin
|
||||||
|
* and σ equal to `sigma`. The size is 6% of `bins` and
|
||||||
|
* the bin width is the same of the intensity histogram:
|
||||||
|
* π/2 / `bins`.
|
||||||
|
*/
|
||||||
|
gsl_histogram* gaussian_kernel(size_t bins, double sigma);
|
||||||
|
|
||||||
|
|
||||||
|
/* `gsl_vector_convolve(a, b, res)` computes the linear
|
||||||
|
* convolution of two vectors. The resulting vector
|
||||||
|
* has size `a->n + b->n - 1` and is stored in `res`.
|
||||||
|
*/
|
||||||
|
int gsl_vector_convolve(gsl_vector *a, gsl_vector *b, gsl_vector *res);
|
||||||
|
|
||||||
|
|
||||||
|
/* Convolves two histograms while keeping the
|
||||||
|
* original bin edges. This is a simple wrapper
|
||||||
|
* function around `gsl_vector_convolve`.
|
||||||
|
*/
|
||||||
|
gsl_histogram* histogram_convolve(gsl_histogram *a, gsl_histogram *b);
|
24
ex-6/dist-plot.py
Executable file
24
ex-6/dist-plot.py
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from pylab import *
|
||||||
|
import sys
|
||||||
|
|
||||||
|
rcParams['font.size'] = 12
|
||||||
|
|
||||||
|
n = int(input())
|
||||||
|
a, b, f = loadtxt(sys.stdin, unpack=True)
|
||||||
|
|
||||||
|
subplot(121)
|
||||||
|
title('FFT')
|
||||||
|
hist(a[:n], insert(b[:n], 0, a[0]), weights=f[:n],
|
||||||
|
histtype='stepfilled', color='#e3c5ca', edgecolor='#92182b')
|
||||||
|
ticklabel_format(style='sci', axis='x', scilimits=(0, 0))
|
||||||
|
|
||||||
|
subplot(122)
|
||||||
|
title('RL')
|
||||||
|
hist(a[n:], insert(b[n:], 0, a[n]), weights=f[n:],
|
||||||
|
histtype='stepfilled', color='#e3c5ca', edgecolor='#92182b')
|
||||||
|
ticklabel_format(style='sci', axis='x', scilimits=(0, 0))
|
||||||
|
|
||||||
|
tight_layout()
|
||||||
|
show()
|
48
ex-6/dist.c
Normal file
48
ex-6/dist.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "dist.h"
|
||||||
|
|
||||||
|
/* Computes the earth mover's distance, aka 1-Wasserstein
|
||||||
|
* metric, of two histograms. It gives the minimum cost
|
||||||
|
* (weight * distance) associated to shifting around the
|
||||||
|
* weights of the bins to turn one histogram into the other.
|
||||||
|
*
|
||||||
|
* For a 1D normalised histogram the distance can be proven
|
||||||
|
* to be equal to the L¹ distance of the CDF. If the bins are
|
||||||
|
* equally sized this can be futher simplified to give a simple
|
||||||
|
* O(n) solution.
|
||||||
|
*/
|
||||||
|
double edm_between(gsl_histogram *a, gsl_histogram *b) {
|
||||||
|
/* For the distance to make sense the
|
||||||
|
* histograms must have the same area.
|
||||||
|
*/
|
||||||
|
normalise(a);
|
||||||
|
normalise(b);
|
||||||
|
|
||||||
|
/* Get the bin width used to compute the cost,
|
||||||
|
* this assumes the histogram have equal ranges
|
||||||
|
* and all bin widths are equal.
|
||||||
|
*/
|
||||||
|
double lower, upper;
|
||||||
|
gsl_histogram_get_range(a, 0, &lower, &upper);
|
||||||
|
double width = upper - lower;
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
double diff = 0;
|
||||||
|
for (int i = 0; i < a->n; i++) {
|
||||||
|
diff += gsl_histogram_get(a, i) - gsl_histogram_get(b, i);
|
||||||
|
sum += fabs(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Multiply the sum by the bin width to
|
||||||
|
* compute the final cost
|
||||||
|
*/
|
||||||
|
return sum * width;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Normalise a histogram: ie rescaling the bin
|
||||||
|
* weights to sum to 1
|
||||||
|
*/
|
||||||
|
void normalise(gsl_histogram *h) {
|
||||||
|
double sum = gsl_histogram_sum(h);
|
||||||
|
gsl_histogram_scale(h, 1/sum);
|
||||||
|
}
|
20
ex-6/dist.h
Normal file
20
ex-6/dist.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include <math.h>
|
||||||
|
#include <gsl/gsl_histogram.h>
|
||||||
|
|
||||||
|
/* Computes the earth mover's distance, aka 1-Wasserstein
|
||||||
|
* metric, of two histograms. It gives the minimum cost
|
||||||
|
* (weight * distance) associated to shifting around the
|
||||||
|
* weights of the bins to turn one histogram into the other.
|
||||||
|
*
|
||||||
|
* For a 1D normalised histogram the distance can be proven
|
||||||
|
* to be equal to the L¹ distance of the CDF. If the bins are
|
||||||
|
* equally sized this can be futher simplified to give a simple
|
||||||
|
* O(n) solution.
|
||||||
|
*/
|
||||||
|
double edm_between(gsl_histogram *a, gsl_histogram *b);
|
||||||
|
|
||||||
|
|
||||||
|
/* Normalise a histogram: ie rescaling the bin
|
||||||
|
* weights to sum to 1
|
||||||
|
*/
|
||||||
|
void normalise(gsl_histogram *h);
|
100
ex-6/main.c
100
ex-6/main.c
@ -2,7 +2,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <gsl/gsl_rng.h>
|
#include <gsl/gsl_rng.h>
|
||||||
#include <gsl/gsl_randist.h>
|
#include <gsl/gsl_randist.h>
|
||||||
#include <gsl/gsl_sf.h>
|
|
||||||
|
#include "common.h"
|
||||||
#include "fft.h"
|
#include "fft.h"
|
||||||
#include "rl.h"
|
#include "rl.h"
|
||||||
|
|
||||||
@ -20,101 +21,6 @@ struct options {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Intensity function parameters */
|
|
||||||
struct param {
|
|
||||||
double a; // aperture radius
|
|
||||||
double k; // wavenumber
|
|
||||||
double e; // wave amplitude
|
|
||||||
double l; // screen distance
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Intensity of the EM field at a large distance
|
|
||||||
* from a circular aperture diffraction of a plane
|
|
||||||
* wave.
|
|
||||||
*
|
|
||||||
* The intensity is given as a function of θ,
|
|
||||||
* the diffraction angle. All the other physical
|
|
||||||
* parameters are controlled by the structure `param`.
|
|
||||||
*
|
|
||||||
* See Fraunhöfer diffraction on "Hect - Optics" for
|
|
||||||
* a derivation of the formula.
|
|
||||||
*/
|
|
||||||
double intensity(double theta, struct param p) {
|
|
||||||
double x = p.k * p.a * sin(theta);
|
|
||||||
double R = p.l / cos(theta);
|
|
||||||
|
|
||||||
/* The intensity function has an eliminable
|
|
||||||
* discontinuoty at the origin, which must
|
|
||||||
* be handled separately.
|
|
||||||
*/
|
|
||||||
if (fabs(x) < 1e-15) {
|
|
||||||
return 0.5 * pow(p.e/R * M_PI * pow(p.a, 2), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
double y = 2*M_PI*pow(p.a, 2) * gsl_sf_bessel_J1(x) / x;
|
|
||||||
return 0.5 * pow(p.e * y/R, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* `gaussian_for(hist, sigma)` generates a histogram
|
|
||||||
* with the same bin width of `hist` but smaller,
|
|
||||||
* with a gaussian PDF with μ at the central bin
|
|
||||||
* and σ equal to `sigma`.
|
|
||||||
*/
|
|
||||||
gsl_histogram* gaussian_for(gsl_histogram *hist, double sigma) {
|
|
||||||
/* Set the size to 6% of the histogram.
|
|
||||||
* Always choose n even to correctly center the
|
|
||||||
* gaussian in the middle.
|
|
||||||
*/
|
|
||||||
size_t n = ((double) hist->n * 6) / 100;
|
|
||||||
n = n % 2 ? n : n+1;
|
|
||||||
|
|
||||||
/* Calculate the (single) bin width assuming
|
|
||||||
* the ranges are uniformely spaced
|
|
||||||
*/
|
|
||||||
double min, max;
|
|
||||||
gsl_histogram_get_range(hist, 0, &min, &max);
|
|
||||||
double dx = max - min;
|
|
||||||
gsl_histogram *res = gsl_histogram_alloc(n);
|
|
||||||
gsl_histogram_set_ranges_uniform(res, 0, dx * n);
|
|
||||||
|
|
||||||
/* The histogram will be such that
|
|
||||||
* the maximum falls in the central bin.
|
|
||||||
*/
|
|
||||||
long int offset = (res->n)/2;
|
|
||||||
|
|
||||||
for (long int i = 0; i < (long int)res->n; i++)
|
|
||||||
res->bin[i] = gsl_ran_gaussian_pdf(
|
|
||||||
((double)(i - offset)) * dx, sigma * dx);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Convolves two histograms while keeping the
|
|
||||||
* original bin edges. This is a simple wrapper
|
|
||||||
* function around `gsl_vector_convolve`.
|
|
||||||
*/
|
|
||||||
gsl_histogram* histogram_convolve(gsl_histogram *a, gsl_histogram *b) {
|
|
||||||
gsl_histogram *res = gsl_histogram_calloc(a->n + b->n - 1);
|
|
||||||
|
|
||||||
/* Set the same edges as `a`*/
|
|
||||||
double max = gsl_histogram_max(a);
|
|
||||||
double min = gsl_histogram_min(a);
|
|
||||||
gsl_histogram_set_ranges_uniform(res, min, max);
|
|
||||||
|
|
||||||
/* Create vector views for everybody */
|
|
||||||
gsl_vector_view va = gsl_vector_view_array(a->bin, a->n);
|
|
||||||
gsl_vector_view vb = gsl_vector_view_array(b->bin, b->n);
|
|
||||||
gsl_vector_view vres = gsl_vector_view_array(res->bin, res->n);
|
|
||||||
|
|
||||||
gsl_vector_convolve(&va.vector, &vb.vector, &vres.vector);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int show_help(char **argv) {
|
int show_help(char **argv) {
|
||||||
fprintf(stderr, "Usage: %s -[hcdoebsrmn]\n", argv[0]);
|
fprintf(stderr, "Usage: %s -[hcdoebsrmn]\n", argv[0]);
|
||||||
fprintf(stderr, " -h\t\tShow this message.\n");
|
fprintf(stderr, " -h\t\tShow this message.\n");
|
||||||
@ -192,7 +98,7 @@ int main(int argc, char **argv) {
|
|||||||
*/
|
*/
|
||||||
fputs("\n# convolution\n", stderr);
|
fputs("\n# convolution\n", stderr);
|
||||||
fputs("1. generating gaussian kernel...", stderr);
|
fputs("1. generating gaussian kernel...", stderr);
|
||||||
gsl_histogram *kernel = gaussian_for(hist, opts.sigma);
|
gsl_histogram *kernel = gaussian_kernel(hist->n, opts.sigma);
|
||||||
fputs("done\n", stderr);
|
fputs("done\n", stderr);
|
||||||
|
|
||||||
fputs("2. convolving...", stderr);
|
fputs("2. convolving...", stderr);
|
||||||
|
51
ex-6/rl.c
51
ex-6/rl.c
@ -1,53 +1,7 @@
|
|||||||
#include "rl.h"
|
|
||||||
#include <gsl/gsl_blas.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
/* `gsl_vector_convolve(a, b, res)` computes the linear
|
#include "rl.h"
|
||||||
* convolution of two vectors. The resulting vector
|
|
||||||
* has size `a->n + b->n - 1` and is stored in `res`.
|
|
||||||
*/
|
|
||||||
int gsl_vector_convolve(gsl_vector *a, gsl_vector *b, gsl_vector *res) {
|
|
||||||
size_t m = a->size;
|
|
||||||
size_t n = b->size;
|
|
||||||
|
|
||||||
/* Vector views for the overlap dot product */
|
|
||||||
gsl_vector_view va, vb;
|
|
||||||
|
|
||||||
/* Reverse `b` before convolving */
|
|
||||||
gsl_vector_reverse(b);
|
|
||||||
|
|
||||||
double dot;
|
|
||||||
size_t start_a, start_b, many;
|
|
||||||
for (size_t i = 0; i < m + n - 1; i++) {
|
|
||||||
/* Calculate the starting indices
|
|
||||||
* of the two vectors a,b overlap.
|
|
||||||
*/
|
|
||||||
if (i < n) start_a = 0;
|
|
||||||
else start_a = i - (n - 1);
|
|
||||||
|
|
||||||
if (i < n) start_b = (n - 1) - i;
|
|
||||||
else start_b = 0;
|
|
||||||
|
|
||||||
/* Calculate how many elements overlap */
|
|
||||||
if (i < n) many = i + 1;
|
|
||||||
else if (i >= m) many = n - (i - m) - 1;
|
|
||||||
else many = n;
|
|
||||||
|
|
||||||
/* Take the dot product of the overlap
|
|
||||||
* and store it in the resulting histogram
|
|
||||||
*/
|
|
||||||
va = gsl_vector_subvector(a, start_a, many);
|
|
||||||
vb = gsl_vector_subvector(b, start_b, many);
|
|
||||||
gsl_blas_ddot(&va.vector, &vb.vector, &dot);
|
|
||||||
gsl_vector_set(res, i, dot);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Put b back together */
|
|
||||||
gsl_vector_reverse(b);
|
|
||||||
|
|
||||||
return GSL_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Performs the Richardson-Lucy deconvolution.
|
/* Performs the Richardson-Lucy deconvolution.
|
||||||
@ -127,7 +81,6 @@ gsl_histogram* rl_deconvolve(
|
|||||||
/* Set NaNs to zero */
|
/* Set NaNs to zero */
|
||||||
for (size_t i = 0; i < rel_blur->size; i++)
|
for (size_t i = 0; i < rel_blur->size; i++)
|
||||||
if (isnan(gsl_vector_get(rel_blur, i))) {
|
if (isnan(gsl_vector_get(rel_blur, i))) {
|
||||||
fprintf(stderr, "gotcha: %ld!!\n", i);
|
|
||||||
double y = (i > 0)? gsl_vector_get(rel_blur, i - 1) : 1;
|
double y = (i > 0)? gsl_vector_get(rel_blur, i - 1) : 1;
|
||||||
gsl_vector_set(rel_blur, i, y);
|
gsl_vector_set(rel_blur, i, y);
|
||||||
}
|
}
|
||||||
|
12
ex-6/rl.h
12
ex-6/rl.h
@ -1,20 +1,10 @@
|
|||||||
#include <gsl/gsl_histogram.h>
|
#include <gsl/gsl_histogram.h>
|
||||||
#include <gsl/gsl_vector.h>
|
|
||||||
|
|
||||||
/* `rl_deconvolve(data, noise, rounds)`
|
/* `rl_deconvolve(data, noise, rounds)`
|
||||||
* tries to deconvolve `noise` from
|
* tries to deconvolve `noise` from
|
||||||
* `data` in `rounds` rounds.
|
* `data` in `rounds` rounds.
|
||||||
* */
|
*/
|
||||||
gsl_histogram* rl_deconvolve(
|
gsl_histogram* rl_deconvolve(
|
||||||
gsl_histogram *data,
|
gsl_histogram *data,
|
||||||
gsl_histogram *noise,
|
gsl_histogram *noise,
|
||||||
size_t rounds);
|
size_t rounds);
|
||||||
|
|
||||||
/* `convolve(a, b)` computes the linear convolution
|
|
||||||
* of two histograms. The convolted histogram
|
|
||||||
* has length `a->n + b->n - 1`.
|
|
||||||
*/
|
|
||||||
int gsl_vector_convolve(
|
|
||||||
gsl_vector *a,
|
|
||||||
gsl_vector *b,
|
|
||||||
gsl_vector *res);
|
|
||||||
|
217
ex-6/test.c
Normal file
217
ex-6/test.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* A pair of `double`s */
|
||||||
|
typedef struct {
|
||||||
|
double a;
|
||||||
|
double b;
|
||||||
|
} pair_t;
|
||||||
|
|
||||||
|
|
||||||
|
int show_help(char **argv) {
|
||||||
|
fprintf(stderr, "Usage: %s -[hcdoebsrmn]\n", argv[0]);
|
||||||
|
fprintf(stderr, " -h\t\tShow this message.\n");
|
||||||
|
fprintf(stderr, " -c\t\tPrint the convolved histogram to stdout.\n");
|
||||||
|
fprintf(stderr, " -d\t\tAttempt and print the deconvolved histogram.\n");
|
||||||
|
fprintf(stderr, " -o\t\tPrint the original histogram to stdout.\n");
|
||||||
|
fprintf(stderr, " -e N\t\tThe number of events. (default: 50000)\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, " -m MODE\tThe deconvolution mode: 'fft' or 'rl'."
|
||||||
|
"(default: 'fft')\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(θ) by reverse
|
||||||
|
* 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 (EDM) between
|
||||||
|
* the deconvolution results and the original,
|
||||||
|
* uncorrupted sample.
|
||||||
|
*
|
||||||
|
* The function returns a pair of the FFT and RL
|
||||||
|
* distances, in this order.
|
||||||
|
*/
|
||||||
|
pair_t 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.
|
||||||
|
*/
|
||||||
|
pair_t dist;
|
||||||
|
dist.a = edm_between(hist, fft_clean);
|
||||||
|
dist.b = edm_between(hist, rl_clean);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
* EDM distances to the original sample.
|
||||||
|
*/
|
||||||
|
double* fft_dist = calloc(opts.num_exps, sizeof(double));
|
||||||
|
double* rl_dist = calloc(opts.num_exps, sizeof(double));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < opts.num_exps; i++) {
|
||||||
|
pair_t dist = experiment(opts, r, kernel);
|
||||||
|
fft_dist[i] = dist.a;
|
||||||
|
rl_dist[i] = dist.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute some statistics of the EDM */
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
/* 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_set_ranges_uniform(fft_hist, fft_min, fft_max);
|
||||||
|
gsl_histogram_set_ranges_uniform(rl_hist, rl_min, rl_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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// print results to stderr
|
||||||
|
fprintf(stderr, "# EDM 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);
|
||||||
|
|
||||||
|
// print histograms to stdout
|
||||||
|
fprintf(stderr, "\n# EDM histogram\n");
|
||||||
|
fprintf(stdout, "%d\n", (int)sqrt(opts.num_exps));
|
||||||
|
gsl_histogram_fprintf(stdout, fft_hist, "%g", "%g");
|
||||||
|
gsl_histogram_fprintf(stdout, rl_hist, "%g", "%g");
|
||||||
|
|
||||||
|
// free memory
|
||||||
|
gsl_rng_free(r);
|
||||||
|
gsl_histogram_free(kernel);
|
||||||
|
gsl_histogram_free(fft_hist);
|
||||||
|
gsl_histogram_free(rl_hist);
|
||||||
|
free(fft_dist);
|
||||||
|
free(rl_dist);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
4
makefile
4
makefile
@ -31,7 +31,9 @@ ex-5/bin/%: ex-5/%.c
|
|||||||
$(CCOMPILE)
|
$(CCOMPILE)
|
||||||
|
|
||||||
ex-6: ex-6/bin/main
|
ex-6: ex-6/bin/main
|
||||||
ex-6/bin/main: ex-6/main.c ex-6/rl.c ex-6/fft.c
|
ex-6/bin/main: ex-6/main.c ex-6/common.c ex-6/rl.c ex-6/fft.c
|
||||||
|
$(CCOMPILE)
|
||||||
|
ex-6/bin/test: ex-6/test.c ex-6/common.c ex-6/rl.c ex-6/fft.c ex-6/dist.c
|
||||||
$(CCOMPILE)
|
$(CCOMPILE)
|
||||||
|
|
||||||
ex-7: ex-7/bin/main ex-7/bin/test
|
ex-7: ex-7/bin/main ex-7/bin/test
|
||||||
|
Loading…
Reference in New Issue
Block a user