Loading [MathJax]/extensions/TeX/AMSsymbols.js
LAL 7.7.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
TriggerInterpolateTest.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with with program; see the file COPYING. If not, write to the
14 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
15 * MA 02110-1301 USA
16 *
17 * Copyright (C) 2012-2020 Leo Singer
18 */
19
20
21#include <complex.h>
22#include <math.h>
23
24#include <gsl/gsl_errno.h>
25#include <gsl/gsl_machine.h>
26#include <gsl/gsl_math.h>
27#include <gsl/gsl_poly.h>
28#include <gsl/gsl_randist.h>
29#include <gsl/gsl_rng.h>
30#include <gsl/gsl_sf_trig.h>
31#include <gsl/gsl_test.h>
32
33#include <lal/TriggerInterpolate.h>
34
35
36static double cpart(double complex z, int i)
37{
38 return ((double *)&z)[i];
39}
40
41
42static double to_phase_angle(double arg)
43{
44 arg = fmod(arg, 2 * M_PI);
45 if (arg < -M_PI)
46 arg += 2 * M_PI;
47 else if (arg > M_PI)
48 arg -= 2 * M_PI;
49 return arg;
50}
51
52
53// Derivative of normalized sinc function.
54static double dsinc(double t)
55{
56 return (cos(M_PI * t) - gsl_sf_sinc(t)) / t;
57}
58
59
60// Second derivative of normalized sinc function.
61static double d2sinc(double t)
62{
63 return -(sin(M_PI * t) * M_PI + 2 * dsinc(t)) / t;
64}
65
66
67// Value and derivative of Lanczos kernel.
68static void fdf_lanczos(double fdf[3], double t, double a)
69{
70 if (t < -a || t > a)
71 {
72 fdf[0] = fdf[1] = fdf[2] = 0;
73 } else {
74 double ta = t / a;
75 double sinct = gsl_sf_sinc(t), sincta = gsl_sf_sinc(ta);
76 double dsinct = dsinc(t), dsincta = dsinc(ta) / a;
77 double d2sinct = d2sinc(t), d2sincta = d2sinc(ta) / gsl_pow_2(a);
78 fdf[0] = sinct * sincta;
79 fdf[1] = sinct * dsincta + sincta * dsinct;
80 fdf[2] = 2 * dsinct * dsincta + sinct * d2sincta + sincta * d2sinct;
81 }
82}
83
84
85static void print_gsl_error(
86 const char *reason,
87 const char *file,
88 int line,
89 __attribute__ ((unused)) int gsl_errno)
90{
91 gsl_stream_printf("ERROR", file, line, reason);
92}
93
94
95#define TEST_WINDOW_SIZE_TOO_SMALL(FUNC, MIN_WINDOW) \
96{ \
97 gsl_error_handler_t *old_handler = gsl_set_error_handler_off(); \
98 for (unsigned int window = 0; window < MIN_WINDOW; window ++) \
99 { \
100 double complex y[2 * window + 1]; \
101 result = FUNC(&tmax, &ymax, &y[window], window); \
102 gsl_test(result != GSL_EINVAL, "%s window=%u returns GSL_EINVAL", #FUNC, window); \
103 } \
104 gsl_set_error_handler(old_handler); \
105}
106
107
108// Draw a random quadratic polynomial for the amplitude:
109// - It has an absolute maximum at t_max, which is in (-1, +1).
110// - The value at the maximum is amp_max.
111// - It is greater than zero over the range (-window, window).
113 gsl_rng *rng,
114 double poly[3],
115 double *t_max,
116 double *amp_max,
117 unsigned int window
118) {
119 double t = gsl_ran_flat(rng, -1, 1);
120 double a = -gsl_ran_gamma(rng, 2, 1);
121 double b = -2 * a * t;
122 double c = gsl_ran_gamma(rng, 2, 1) - a * window * (window + 2 * fabs(t));
123 poly[0] = c;
124 poly[1] = b;
125 poly[2] = a;
126 *t_max = t;
127 *amp_max = c - 0.25 * gsl_pow_2(b) / a;
128}
129
130
131// Draw a random linear polynomial for the phase:
132// - The absolute value of the slope is no greater than pi.
133static void ran_arg_poly_linear(gsl_rng *rng, double poly[2])
134{
135 poly[0] = gsl_ran_flat(rng, -M_PI, M_PI);
136 poly[1] = gsl_ran_flat(rng, -M_PI, M_PI);
137}
138
139
140int main(__attribute__ ((unused)) int argc, __attribute__ ((unused)) char **argv)
141{
142 gsl_rng *rng = gsl_rng_alloc(gsl_rng_default);
143 int result;
144 double complex ymax;
145 double tmax;
146
147 gsl_set_error_handler(print_gsl_error);
148
153
154 /* Test Lanczos interpolant. */
155 for (unsigned int window = 1; window < 50; window ++)
156 {
157 unsigned int len = 2 * window + 1;
158 double complex y[len], (*data) = &y[window], central_datum;
159
160 // Random values for all samples
161 for (unsigned int i = 0; i < len; i ++)
162 y[i] = crect(gsl_ran_gaussian(rng, 1), gsl_ran_gaussian(rng, 1));
163
164 // Normalized value of central datum
165 central_datum = data[0];
166 central_datum /= cabs(central_datum);
167
168 // Pick a random maximum time and phase.
169 double tmax_expected = gsl_ran_flat(rng, -1, 1);
170
171 // Solve for the absolute value of the central point that produces
172 // a stationary point at tmax_expected.
173 double complex pdp[3] = {0}, qdq[3];
174 double a = 0, b = 0, c = 0, a1 = 0, b1 = 0, c1 = 0, roots[2], fdf[3];
175 for (unsigned int i = 1; i <= window; i ++)
176 {
177 for (int sign = -1; sign <= 1; sign += 2)
178 {
179 double complex datum = data[sign * (int)i];
180 fdf_lanczos(fdf, tmax_expected - sign * (int)i, window);
181 for (int deriv = 0; deriv < 3; deriv++)
182 pdp[deriv] += datum * fdf[deriv];
183 }
184 }
185 fdf_lanczos(fdf, tmax_expected, window);
186 for (int deriv = 0; deriv < 3; deriv ++)
187 qdq[deriv] = central_datum * fdf[deriv];
188 for (int part = 0; part < 2; part ++)
189 {
190 // Quadratic equation for stationary point
191 a += cpart(qdq[0], part) * cpart(qdq[1], part);
192 b += cpart(pdp[0], part) * cpart(qdq[1], part) + cpart(pdp[1], part) * cpart(qdq[0], part);
193 c += cpart(pdp[0], part) * cpart(pdp[1], part);
194
195 // Quadratic equation for second derivative at stationary point
196 a1 += gsl_pow_2(cpart(qdq[1], part)) + cpart(qdq[0], part) * cpart(qdq[2], part);
197 b1 += 2 * cpart(pdp[1], part) * cpart(qdq[1], part) + cpart(pdp[0], part) * cpart(qdq[2], part) + cpart(pdp[2], part) * cpart(qdq[0], part);
198 c1 += gsl_pow_2(cpart(pdp[1], part)) + cpart(pdp[0], part) * cpart(pdp[2], part);
199 }
200 int nroots = gsl_poly_solve_quadratic(a, b, c, &roots[0], &roots[1]);
201 for (int iroot = 0; iroot < nroots; iroot ++)
202 {
203 double root = roots[iroot];
204 double complex ymax_expected = pdp[0] + root * qdq[0];
205 double abs_expected = cabs(ymax_expected);
206 data[0] = root * central_datum;
207
208 double second_deriv = a1 * gsl_pow_2(root) + b1 * root + c1;
209
210 // Skip if this is a saddle point or local minimum.
211 if (second_deriv >= 0)
212 continue;
213
214 // Skip if the local maximum is not greater than the sample points.
215 if (abs_expected <= GSL_MAX_DBL(cabs(data[0]), GSL_MAX_DBL(cabs(data[-1]), cabs(data[1]))))
216 continue;
217
218 // Skip if the new sample point is not greater than the
219 // two enclosing points.
220 if (fabs(root) <= GSL_MAX_DBL(cabs(data[-1]), cabs(data[1])))
221 continue;
222
223 result = XLALTriggerInterpolateLanczos(&tmax, &ymax, data, window);
224 gsl_test(result, "XLALTriggerInterpolateLanczos random signal window=%u return value", window);
225 gsl_test_abs(tmax, tmax_expected, 1e-6, "XLALTriggerInterpolateLanczos random signal window=%u tmax", window);
226 gsl_test_abs(cabs(ymax), cabs(ymax_expected), 1e-6, "XLALTriggerInterpolateLanczos random signal window=%u abs", window);
227 gsl_test_abs(carg(ymax), carg(ymax_expected), 1e-6, "XLALTriggerInterpolateLanczos random signal window=%u arg", window);
228 }
229 }
230
231 /* Test on a signal whose absolute value is itself quadratic, and that has a maximum in [-1, 1]. */
232 for (unsigned int window = 1; window < 100; window ++)
233 {
234 unsigned int len = 2 * window + 1;
235 double amp_poly[3], arg_poly[2], tmax_expected, amp_expected;
236 ran_amp_poly_quadratic(rng, amp_poly, &tmax_expected, &amp_expected, window);
237 ran_arg_poly_linear(rng, arg_poly);
238 double arg_expected = to_phase_angle(gsl_poly_eval(arg_poly, 2, tmax_expected));
239 double complex y[len];
240 for (unsigned int i = 0; i < len; i ++)
241 {
242 double x = (int)i - (int)window;
243 double amp = gsl_poly_eval(amp_poly, 3, x);
244 double arg = gsl_poly_eval(arg_poly, 2, x);
245 y[i] = cpolar(amp, arg);
246 }
247
248 result = XLALTriggerInterpolateQuadraticFit(&tmax, &ymax, &y[window], window);
249 gsl_test(result, "XLALTriggerInterpolateQuadraticFit window=%u return value", window);
250 gsl_test_abs(tmax, tmax_expected, 1e3 * GSL_DBL_EPSILON, "XLALTriggerInterpolateQuadraticFit quadratic signal window=%u tmax", window);
251 gsl_test_abs(cabs(ymax), amp_expected, 1e6 * GSL_DBL_EPSILON, "XLALTriggerInterpolateQuadraticFit quadratic signal window=%u abs", window);
252 gsl_test_abs(carg(ymax), arg_expected, 1e6 * GSL_DBL_EPSILON, "XLALTriggerInterpolateQuadraticFit quadratic signal window=%u arg", window);
253
254 if (window >= 2)
255 {
256 result = XLALTriggerInterpolateCubicSplineAmpPhase(&tmax, &ymax, &y[window], window);
257 gsl_test(result, "XLALTriggerInterpolateCubicSplineAmpPhase quadratic signal window=%u return value", window);
258 gsl_test_abs(tmax, tmax_expected, 1e5 * GSL_DBL_EPSILON, "XLALTriggerInterpolateCubicSplineAmpPhase quadratic signal window=%u tmax", window);
259 gsl_test_abs(cabs(ymax), amp_expected, 1e5 * GSL_DBL_EPSILON, "XLALTriggerInterpolateCubicSplineAmpPhase quadratic signal window=%u abs", window);
260 gsl_test_abs(carg(ymax), arg_expected, 1e5 * GSL_DBL_EPSILON, "XLALTriggerInterpolateCubicSplineAmpPhase quadratic signal window=%u arg", window);
261 }
262 }
263
264 /* Test on a signal whose real part is quadratic. */
265 for (int run = 0; run < 100; run ++)
266 {
267 unsigned int window = 2, len = 2 * window + 1;
268 double amp_poly[3], tmax_expected, re_expected, im_expected = 0;
269 ran_amp_poly_quadratic(rng, amp_poly, &tmax_expected, &re_expected, window);
270 double complex y[len];
271 for (unsigned int i = 0; i < len; i ++)
272 {
273 double x = (int)i - (int)window;
274 y[i] = gsl_poly_eval(amp_poly, 3, x);
275 }
276
277 result = XLALTriggerInterpolateCubicSpline(&tmax, &ymax, &y[window], window);
278 gsl_test(result, "XLALTriggerInterpolateCubicSpline quadratic real part window=%u return value", window);
279 gsl_test_abs(tmax, tmax_expected, 1e6 * GSL_DBL_EPSILON, "XLALTriggerInterpolateCubicSpline quadratic real part window=%u tmax", window);
280 gsl_test_abs(creal(ymax), re_expected, 1e6 * GSL_DBL_EPSILON, "XLALTriggerInterpolateCubicSpline quadratic real part window=%u real", window);
281 gsl_test_abs(cimag(ymax), im_expected, 0, "XLALTriggerInterpolateCubicSpline quadratic real part window=%u imag", window);
282 }
283
284 /* Test on a signal whose absolute value is linear. */
285 for (unsigned int window = 1; window < 100; window ++)
286 {
287 unsigned int len = 2 * window + 1;
288 int sign = 2 * gsl_ran_bernoulli(rng, 0.5) - 1;
289 double complex y[len];
290 double tmax_expected = sign;
291 double real_expected = window + 1;
292 double imag_expected = 0;
293 for (unsigned int i = 0; i < len; i ++)
294 {
295 double x = (int)i - (int)window;
296 y[i] = crect(sign * x + window, 0);
297 }
298
299 result = XLALTriggerInterpolateQuadraticFit(&tmax, &ymax, &y[window], window);
300 gsl_test(result, "XLALTriggerInterpolateQuadraticFit linear signal window=%u return value", window);
301 gsl_test_abs(tmax, tmax_expected, 0, "XLALTriggerInterpolateQuadraticFit linear signalwindow=%u tmax", window);
302 gsl_test_abs(creal(ymax), real_expected, 0, "XLALTriggerInterpolateQuadraticFit linear signal window=%u real", window);
303 gsl_test_abs(cimag(ymax), imag_expected, 0, "XLALTriggerInterpolateQuadraticFit linear signal window=%u imag", window);
304
305 if (window >= 2)
306 {
307 result = XLALTriggerInterpolateCubicSpline(&tmax, &ymax, &y[window], window);
308 gsl_test(result, "XLALTriggerInterpolateCubicSpline linear signal window=%u return value", window);
309 gsl_test_abs(tmax, tmax_expected, 0, "XLALTriggerInterpolateCubicSpline linear signal window=%u tmax", window);
310 gsl_test_abs(creal(ymax), real_expected, 0, "XLALTriggerInterpolateCubicSpline linear signal window=%u imag", window);
311 gsl_test_abs(cimag(ymax), imag_expected, 0, "XLALTriggerInterpolateCubicSpline linear signal window=%u imag", window);
312
313 result = XLALTriggerInterpolateCubicSplineAmpPhase(&tmax, &ymax, &y[window], window);
314 gsl_test(result, "XLALTriggerInterpolateCubicSplineAmpPhase linear signal window=%u return value", window);
315 gsl_test_abs(tmax, tmax_expected, 0, "XLALTriggerInterpolateCubicSplineAmpPhase linear signal window=%u tmax", window);
316 gsl_test_abs(creal(ymax), real_expected, 0, "XLALTriggerInterpolateCubicSplineAmpPhase linear signal window=%u imag", window);
317 gsl_test_abs(cimag(ymax), imag_expected, 0, "XLALTriggerInterpolateCubicSplineAmpPhase linear signal window=%u imag", window);
318 }
319 }
320
321 /* NearestNeighbor */
322 for (int i = 0; i < 100; i ++)
323 {
324 double complex y = crect(gsl_ran_gaussian(rng, 1), gsl_ran_gaussian(rng, 1));
325 result = XLALTriggerInterpolateNearestNeighbor(&tmax, &ymax, &y, 0);
326 gsl_test(result, "XLALTriggerInterpolateNearestNeighbor return value");
327 gsl_test_abs(tmax, 0, 0, "XLALTriggerInterpolateNearestNeighbor tmax");
328 gsl_test_abs(creal(ymax), creal(y), 0, "XLALTriggerInterpolateNearestNeighbor real part");
329 gsl_test_abs(cimag(ymax), cimag(y), 0, "XLALTriggerInterpolateNearestNeighbor imag part");
330 }
331
332 return gsl_test_summary();
333}
static const UINT4 c1
Definition: LALCityHash.c:146
static int sign(int s)
Definition: LALStringTest.c:27
int XLALTriggerInterpolateNearestNeighbor(double *tmax, double complex *ymax, const double complex *data, __attribute__((unused)) unsigned int window)
static double d2sinc(double t)
static double cpart(double complex z, int i)
static void ran_amp_poly_quadratic(gsl_rng *rng, double poly[3], double *t_max, double *amp_max, unsigned int window)
int main(__attribute__((unused)) int argc, __attribute__((unused)) char **argv)
#define TEST_WINDOW_SIZE_TOO_SMALL(FUNC, MIN_WINDOW)
static void print_gsl_error(const char *reason, const char *file, int line, __attribute__((unused)) int gsl_errno)
static double to_phase_angle(double arg)
static double dsinc(double t)
static void fdf_lanczos(double fdf[3], double t, double a)
static void ran_arg_poly_linear(gsl_rng *rng, double poly[2])
#define __attribute__(x)
Definition: getdate.c:146
#define crect(re, im)
Construct a COMPLEX16 from real and imaginary parts.
#define cpolar(r, th)
Construct a COMPLEX16 from polar modulus and argument.
static const INT4 a
Definition: Random.c:79
int XLALTriggerInterpolateQuadraticFit(double *tmax, double complex *ymax, const double complex *data, unsigned int window)
Quadratic fit.
int XLALTriggerInterpolateCubicSplineAmpPhase(double *t, double complex *y, const double complex *data, unsigned int window)
Catmull-Rom cubic spline interpolation on amplitude phase.
int XLALTriggerInterpolateCubicSpline(double *t, double complex *y, const double complex *data, unsigned int window)
Catmull-Rom cubic spline interpolation.
int XLALTriggerInterpolateLanczos(double *tmax, double complex *ymax, const double complex *data, unsigned int window)
Lanczos interpolation.