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
TriggerInterpolate.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#include <complex.h>
21#include <string.h>
22
23#include <gsl/gsl_errno.h>
24#include <gsl/gsl_fit.h>
25#include <gsl/gsl_multifit.h>
26#include <gsl/gsl_min.h>
27#include <gsl/gsl_nan.h>
28#include <gsl/gsl_poly.h>
29#include <gsl/gsl_sf_trig.h>
30#include <gsl/gsl_statistics.h>
31#include <gsl/gsl_version.h>
32
33#include <lal/TriggerInterpolate.h>
34
35#define STRINGIZE(s) #s
36
37
38/*
39 * General functions
40 */
41
42
43/* Unwrap phase angles. All elements of arg must be between -M_PI and M_PI. */
44static void unwrap(double *arg, size_t n)
45{
46 double prev = arg[0];
47 long winds = 0;
48 for (size_t i = 1; i < n; i ++)
49 {
50 double next = arg[i], delta = next - prev;
51
52 if (delta > M_PI)
53 winds -= 1;
54 else if (delta < -M_PI)
55 winds += 1;
56 arg[i] += winds * 2 * M_PI;
57
58 prev = next;
59 }
60}
61
62
63static void cabs_carg_unwrapped(double *amp, double *arg, const double complex *y, size_t n)
64{
65 for (size_t i = 0; i < n; i ++)
66 {
67 amp[i] = cabs(y[i]);
68 arg[i] = carg(y[i]);
69 }
70
71 unwrap(arg, n);
72}
73
74
75/* Strip leading zero coefficients of a polynomial.
76 * Pass the number of coefficients as n. The new number of coefficients is returned. */
77static size_t poly_strip(const double *a, size_t n)
78{
79 for (; n > 0 && a[n - 1] == 0; n--)
80 ; /* loop body intentionally a no-op */
81 return n;
82}
83
84
85/* Polynomial multiply-accumulate.
86 * The resulting polynomial has length (na + nb - 1). */
87static void poly_mac(double *y, const double *a, size_t na, const double *b, size_t nb)
88{
89 /* Compute convolution. */
90 for (size_t ia = 0; ia < na; ia++)
91 for (size_t ib = 0; ib < nb; ib++)
92 y[ia + ib] += a[ia] * b[ib];
93}
94
95
96/* Compute derivative of a polynomial.
97 * The resulting polynomial has length (n - 1). */
98static void poly_der(double *y, const double *a, size_t n)
99{
100 for (size_t i = 0; i < n - 1; i ++)
101 y[i] = (i + 1) * a[i + 1];
102}
103
104
105/* Construct Catmull-Rom inteprolating polynomial. */
106static void poly_interp(double *a, const double y[4])
107{
108 a[3] = 0.5 * (y[3] - y[0]) + 1.5 * (y[1] - y[2]);
109 a[2] = y[0] - 2.5 * y[1] + 2 * y[2] - 0.5 * y[3];
110 a[1] = 0.5 * (y[2] - y[0]);
111 a[0] = y[1];
112}
113
114
115/* Find all real roots of a polynomial.
116 * For quartic and higher order polynomials, use gsl_poly_complex_solve,
117 * but allocate the workspace on the stack.
118 *
119 * Since the workspace for a polynomial of n coefficients is an (n-1)x(n-1)
120 * matrix, to avoid exhausting stack memory, this function should only be
121 * used for low-order (n<100) polynomials.
122 */
123static int poly_real_roots(size_t *nroots, double *r, const double *a, size_t n) {
124 n = poly_strip(a, n);
125
126 if (n <= 3) {
127
128 /* Handle polynomials of quadratic or lower order.
129 * gsl_poly_solve_quadratic itself handles n=0, n=1, and n=2
130 * as special cases. */
131 *nroots = gsl_poly_solve_quadratic(
132 a[2], a[1], a[0],
133 &r[0], &r[1]);
134
135 return GSL_SUCCESS;
136
137 } else if (n <= 4) {
138
139 /* Handle cubic polynomials. */
140 *nroots = gsl_poly_solve_cubic(
141 a[2] / a[3], a[1] / a[3], a[0] / a[3],
142 &r[0], &r[1], &r[2]);
143
144 return GSL_SUCCESS;
145
146 } else {
147
148 /* Handle polynomials of quartic or higher order. */
149 double complex complex_roots[n - 1];
150 double workspace_matrix[n - 1][n - 1];
151 gsl_poly_complex_workspace workspace = {n - 1, *workspace_matrix};
152
153 int result = gsl_poly_complex_solve(a, n, &workspace,
154 (gsl_complex_packed_ptr) complex_roots);
155
156 if (result == GSL_SUCCESS)
157 {
158 *nroots = 0;
159 for (size_t i = 0; i + 1 < n; i ++)
160 if (cimag(complex_roots[i]) == 0)
161 r[(*nroots)++] = creal(complex_roots[i]);
162 }
163 return result;
164
165 }
166}
167
168
169/* Find least-squares fit of a polynomial to data.
170 * For quadratic and higher order polynomials, workspaces are allocated
171 * on the stack, so this function should not be used for large problems
172 * (e.g., polynomial order times number of data points much greater than 10k).
173 */
174static int poly_fit(double *a, size_t na, const double *x, const double *y, size_t n)
175{
176 if (na > n)
177 {
178 GSL_ERROR("Requested polynomial with more coefficients than data points", GSL_EINVAL);
179 } else if (na == 0) {
180 GSL_ERROR("Requested a polynomial with zero coefficients", GSL_EINVAL);
181 } else if (na == 1) {
182 *a = gsl_stats_mean(y, 1, n);
183 return GSL_SUCCESS;
184 } else if (na == 2) {
185 double unused;
186 return gsl_fit_linear(x, 1, y, 1, n, &a[0], &a[1], &unused, &unused, &unused, &unused);
187 } else {
188 /* Set up solver using stack memory. */
189 double X[n][na], A[n][na], Q[na][na], QSI[na][na], C[na][na], S[na], t[n], xt[na], D[na], unused;
190 gsl_matrix_view Xview = gsl_matrix_view_array(*X, n, na);
191 gsl_matrix_view Aview = gsl_matrix_view_array(*A, n, na);
192 gsl_matrix_view Qview = gsl_matrix_view_array(*Q, na, na);
193 gsl_matrix_view QSIview = gsl_matrix_view_array(*QSI, na, na);
194 gsl_matrix_view Cview = gsl_matrix_view_array(*C, na, na);
195 gsl_vector_view Sview = gsl_vector_view_array(S, na);
196 gsl_vector_view tview = gsl_vector_view_array(t, n);
197 gsl_vector_view xtview = gsl_vector_view_array(xt, na);
198 gsl_vector_view Dview = gsl_vector_view_array(D, na);
199 gsl_vector_const_view yview = gsl_vector_const_view_array(y, n);
200 gsl_vector_view cview = gsl_vector_view_array(a, na);
201
202 gsl_multifit_linear_workspace workspace = {
203 .A = &Aview.matrix, .Q = &Qview.matrix, .QSI = &QSIview.matrix, .S
204 = &Sview.vector, .t = &tview.vector, .xt = &xtview.vector, .D =
205 &Dview.vector};
206
207 #if GSL_MAJOR_VERSION < 2
208 workspace.n = n;
209 workspace.p = na;
210 #else
211 workspace.nmax = n;
212 workspace.pmax = na;
213 #endif
214
215 for (size_t i = 0; i < n; i ++)
216 for (size_t ia = 0; ia < na; ia ++)
217 X[i][ia] = gsl_pow_int(x[i], ia);
218
219 return gsl_multifit_linear(&Xview.matrix, &yview.vector,
220 &cview.vector, &Cview.matrix, &unused, &workspace);
221 }
222}
223
224
225/*
226 * CubicSpline
227 */
228
229
230#define CUBIC_SPLINE_WINDOW_SIZE 2
231
232
234 double *t,
235 double complex *y,
236 const double complex *data,
237 unsigned int window)
238{
239 if (window < CUBIC_SPLINE_WINDOW_SIZE)
240 GSL_ERROR(
241 "Window size must be >= " STRINGIZE(CUBIC_SPLINE_WINDOW_SIZE),
242 GSL_EINVAL);
243
244 int result;
245
246 /* Find which among samples -1, 0, +1 has the maximum amplitude. */
247 double complex y_max = data[-1];
248 double amp_max = cabs(y_max);
249 double t_max = -1;
250 for (int i = 0; i < 2; i ++)
251 {
252 double amp = cabs(data[i]);
253 if (amp > amp_max)
254 {
255 t_max = i;
256 amp_max = amp;
257 y_max = data[i];
258 }
259 }
260
261 /* Fit interpolant to samples -2 through 1 and samples -1 through 2. */
262 for (int i = -1; i < 1; i ++)
263 {
264 double polys[2][4], deriv[6] = {0}, roots[5];
265 size_t nroots;
266
267 /* Take derivative of absolute value of interpolant.
268 * If the interpolant is z(t) = a(t) + i * b(t),
269 * then d(|z(t)|^2)/dt = 2 a(t) a'(t) + 2 b(t) b'(t).
270 * The factor of 2 is dropped because we are only interested in finding
271 * the zeros. */
272 for (int j = 0; j < 2; j ++)
273 {
274 double data_part[4], deriv_part[3];
275 for (int k = 0; k < 4; k ++)
276 data_part[k] = ((const double *) data)[(k + i - 1) * 2 + j];
277 poly_interp(polys[j], data_part);
278 poly_der(deriv_part, polys[j], 4);
279 poly_mac(deriv, polys[j], 4, deriv_part, 3);
280 }
281
282 result = poly_real_roots(&nroots, roots, deriv, 6);
283 if (result != GSL_SUCCESS)
284 return result;
285
286 for (size_t j = 0; j < nroots; j ++)
287 {
288 if (roots[j] > 0 && roots[j] < 1)
289 {
290 double complex y_new = crect(
291 gsl_poly_eval(polys[0], 4, roots[j]),
292 gsl_poly_eval(polys[1], 4, roots[j]));
293 double amp = cabs(y_new);
294 if (amp > amp_max)
295 {
296 t_max = roots[j] + i;
297 y_max = y_new;
298 amp_max = amp;
299 }
300 }
301 }
302 }
303
304 *t = t_max;
305 *y = y_max;
306 return result;
307}
308
309
310/*
311 * CubicSplineAmpPhase
312 */
313
314
316 double *t,
317 double complex *y,
318 const double complex *data,
319 unsigned int window)
320{
321 if (window < CUBIC_SPLINE_WINDOW_SIZE)
322 GSL_ERROR(
323 "Window size must be >= " STRINGIZE(CUBIC_SPLINE_WINDOW_SIZE),
324 GSL_EINVAL);
325
326 double amps[2 * CUBIC_SPLINE_WINDOW_SIZE + 1];
327 double args[2 * CUBIC_SPLINE_WINDOW_SIZE + 1];
328 double t_max, amp_max, arg_max;
330 amps, args,
333
334 /* Find which among samples -1, 0, +1 has the maximum amplitude. */
335 t_max = -1;
336 amp_max = amps[1];
337 arg_max = args[1];
338 for (int i = 0; i < 2; i ++)
339 {
340 if (amps[i + 2] > amp_max)
341 {
342 t_max = i;
343 amp_max = amps[i + 2];
344 arg_max = args[i + 2];
345 }
346 }
347
348 /* Fit interpolant to samples -2 through 1 and samples -1 through 2. */
349 for (int i = -1; i < 1; i ++)
350 {
351 double amp_poly[4], arg_poly[4], der[3], roots[2];
352 size_t nroots;
353 int result;
354
355 poly_interp(amp_poly, &amps[i + 1]);
356 poly_interp(arg_poly, &args[i + 1]);
357 poly_der(der, amp_poly, 4);
358
359 result = poly_real_roots(&nroots, roots, der, 3);
360 if (result != GSL_SUCCESS)
361 return result;
362
363 for (size_t j = 0; j < nroots; j ++)
364 {
365 double amp, arg;
366 if (roots[j] < 0 || roots[j] > 1)
367 continue;
368
369 amp = gsl_poly_eval(amp_poly, 4, roots[j]);
370 arg = gsl_poly_eval(arg_poly, 4, roots[j]);
371 if (amp > amp_max)
372 {
373 amp_max = amp;
374 arg_max = arg;
375 t_max = roots[j] + i;
376 }
377 }
378 }
379
380 *t = t_max;
381 *y = cpolar(amp_max, arg_max);
382 return GSL_SUCCESS;
383}
384
385
386/*
387 * Lanczos
388 */
389
390
391/* Data structure providing arguments for minimizer cost function. */
392typedef struct {
393 const double complex *data;
394 unsigned int window;
396
397
398/* The Lanczos kernel. */
399static double lanczos(double t, double a)
400{
401 if (t < -a || t > a)
402 return 0;
403
404 return gsl_sf_sinc(t) * gsl_sf_sinc(t / a);
405}
406
407
408/* The Lanczos reconstruction filter interpolant. */
409static double complex lanczos_interpolant(double t, const lanczos_params *params)
410{
411 double complex ret = 0;
412 for (int i = -(int)params->window; i <= (int)params->window; i ++)
413 ret += lanczos(t - i, params->window) * params->data[i];
414 return ret;
415}
416
417
418/* The cost function to minimize. */
419static double lanczos_cost(double t, void *params)
420{
421 return -cabs(lanczos_interpolant(t, params));
422}
423
424
426 double *tmax,
427 double complex *ymax,
428 const double complex *data,
429 unsigned int window)
430{
431 if (window < 1)
432 GSL_ERROR("Window size must be >= 1", GSL_EINVAL);
433
434 /* Set up minimizer state on stack. */
435 const gsl_min_fminimizer_type *fminimizer_type = gsl_min_fminimizer_brent;
436 char fminimizer_state[fminimizer_type->size];
437 gsl_min_fminimizer fminimizer = {.type = fminimizer_type, .state = fminimizer_state};
438
439 lanczos_params params = {data, window};
440 gsl_function func = {lanczos_cost, &params};
441 int result;
442
443 result = gsl_min_fminimizer_set_with_values(&fminimizer, &func,
444 0, -cabs(data[0]), -1, -cabs(data[-1]), 1, -cabs(data[1]));
445 if (result != GSL_SUCCESS)
446 GSL_ERROR("failed to initialize minimizer", result);
447
448 do {
449 result = gsl_min_fminimizer_iterate(&fminimizer);
450 if (result != GSL_SUCCESS)
451 GSL_ERROR("failed to perform minimizer iteration", result);
452
453 double t1 = gsl_min_fminimizer_x_lower(&fminimizer);
454 double t2 = gsl_min_fminimizer_x_upper(&fminimizer);
455 result = gsl_min_test_interval(t1, t2, 1e-6, 0);
456 } while (result == GSL_CONTINUE);
457
458 if (result != GSL_SUCCESS)
459 GSL_ERROR("failed to perform minimizer convergence test", result);
460
461 *tmax = gsl_min_fminimizer_x_minimum(&fminimizer);
462 *ymax = lanczos_interpolant(*tmax, &params);
463 return result;
464}
465
466
467/*
468 * Nearest neighbor
469 */
470
471
473 double *tmax,
474 double complex *ymax,
475 const double complex *data,
476 __attribute__ ((unused)) unsigned int window)
477{
478 *tmax = 0;
479 *ymax = *data;
480 return GSL_SUCCESS;
481}
482
483
484/*
485 * Quadratic fit
486 */
487
488
490 double *tmax,
491 double complex *ymax,
492 const double complex *data,
493 unsigned int window)
494{
495 if (window < 1)
496 GSL_ERROR("Window size must be >= 1", GSL_EINVAL);
497
498 int result, i, nsamples = 2 * window + 1;
499 double t, t_new, amp, arg, x[nsamples], amps[nsamples], args[nsamples], amp_poly[3], arg_poly[2];
500
501 cabs_carg_unwrapped(amps, args, data - (int)window, nsamples);
502
503 i = (amps[window - 1] > amps[window + 1]) ? -1 : 1;
504 t = i;
505 amp = amps[window + i];
506 arg = args[window + i];
507
508 for (i = 0; i < nsamples; i ++)
509 x[i] = i - (int)window;
510
511 result = poly_fit(amp_poly, 3, x, amps, nsamples);
512 if (result != GSL_SUCCESS)
513 return result;
514
515 result = poly_fit(arg_poly, 2, x, args, nsamples);
516 if (result != GSL_SUCCESS)
517 return result;
518
519 if (amp_poly[2] == 0)
520 t_new = GSL_SIGN(amp_poly[1]);
521 else
522 t_new = -0.5 * amp_poly[1] / amp_poly[2];
523
524 if (t_new > -1 && t_new < 1)
525 {
526 double amp_new = gsl_poly_eval(amp_poly, 3, t_new);
527 double arg_new = gsl_poly_eval(arg_poly, 2, t_new);
528 if (amp_new > amp)
529 {
530 t = t_new;
531 amp = amp_new;
532 arg = arg_new;
533 }
534 }
535
536 *tmax = t;
537 *ymax = cpolar(amp, arg);
538 return result;
539}
static int poly_real_roots(size_t *nroots, double *r, const double *a, size_t n)
#define CUBIC_SPLINE_WINDOW_SIZE
static double lanczos_cost(double t, void *params)
static double lanczos(double t, double a)
static void poly_interp(double *a, const double y[4])
#define STRINGIZE(s)
static void unwrap(double *arg, size_t n)
static int poly_fit(double *a, size_t na, const double *x, const double *y, size_t n)
static void cabs_carg_unwrapped(double *amp, double *arg, const double complex *y, size_t n)
static size_t poly_strip(const double *a, size_t n)
static void poly_mac(double *y, const double *a, size_t na, const double *b, size_t nb)
static void poly_der(double *y, const double *a, size_t n)
int XLALTriggerInterpolateNearestNeighbor(double *tmax, double complex *ymax, const double complex *data, __attribute__((unused)) unsigned int window)
static double complex lanczos_interpolant(double t, const lanczos_params *params)
static double Q(double mu, double x)
Definition: XLALMarcumQ.c:99
#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 r
Definition: Random.c:82
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.
const double complex * data
unsigned int window