File indexing completed on 2024-05-12 15:27:06

0001 /***************************************************************************
0002     File                 : nsl_corr.c
0003     Project              : LabPlot
0004     Description          : NSL discrete correlation functions
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2018 by Stefan Gerlach (stefan.gerlach@uni.kn)
0007 
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "nsl_corr.h"
0030 #include "nsl_common.h"
0031 #include <gsl/gsl_cblas.h>
0032 #include <gsl/gsl_fft_halfcomplex.h>
0033 #ifdef HAVE_FFTW3
0034 #include <fftw3.h>
0035 #endif
0036 
0037 const char* nsl_corr_type_name[] = {i18n("linear (zero-padded)"), i18n("circular")};
0038 const char* nsl_corr_norm_name[] = {i18n("none"), i18n("biased"), i18n("unbiased"), i18n("coeff")};
0039 
0040 int nsl_corr_correlation(double s[], size_t n, double r[], size_t m, nsl_corr_type_type type, nsl_corr_norm_type normalize, double out[]) {
0041     return nsl_corr_fft_type(s, n, r, m, type, normalize, out);
0042 }
0043 
0044 int nsl_corr_fft_type(double s[], size_t n, double r[], size_t m, nsl_corr_type_type type, nsl_corr_norm_type normalize, double out[]) {
0045     size_t i, size, N = GSL_MAX(n, m), maxlag = N - 1;
0046     if (type == nsl_corr_type_linear)
0047         size = maxlag + N;
0048     else    // circular
0049         size = N;
0050 
0051     size_t oldsize = size;
0052 #ifdef HAVE_FFTW3
0053     // already zero-pad here for FFT method and FFTW r2c
0054     size = 2*(size/2+1);
0055 #endif
0056 
0057     // zero-padded arrays
0058     double *stmp = (double*)malloc(size*sizeof(double));
0059     if (stmp == NULL) {
0060         printf("nsl_corr_fft_type(): ERROR allocating memory for 'stmp'!\n");
0061         return -1;
0062     }
0063 
0064     double *rtmp = (double*)malloc(size*sizeof(double));
0065     if (rtmp == NULL) {
0066         free(stmp);
0067         printf("nsl_corr_fft_type(): ERROR allocating memory for 'rtmp'!\n");
0068         return -1;
0069     }
0070 
0071     if (type == nsl_corr_type_linear) {
0072         for (i = 0; i < maxlag; i++)
0073             stmp[i] = 0.;
0074         for (i = 0; i < n; i++)
0075             stmp[i+maxlag] = s[i];
0076         for (i = n+maxlag; i < size; i++)
0077             stmp[i] = 0;
0078         for (i = 0; i < m; i++)
0079             rtmp[i] = r[i];
0080         for (i = m; i < size; i++)
0081             rtmp[i] = 0;
0082     } else {    // circular
0083         for (i = 0; i < n; i++)
0084             stmp[i] = s[i];
0085         for (i = n; i < N; i++)
0086             stmp[i] = 0.;
0087         for (i = 0; i < m; i++)
0088             rtmp[i] = r[i];
0089         for (i = m; i < N; i++)
0090             rtmp[i] = 0.;
0091     }
0092 
0093     int status;
0094 #ifdef HAVE_FFTW3   // already wraps output
0095     status = nsl_corr_fft_FFTW(stmp, rtmp, oldsize, out);
0096 #else   // GSL
0097     status = nsl_corr_fft_GSL(stmp, rtmp, size, out);
0098 #endif
0099 
0100     free(stmp);
0101     free(rtmp);
0102 
0103     /* normalization */
0104     switch (normalize) {
0105     case nsl_corr_norm_none:
0106         break;
0107     case nsl_corr_norm_biased:
0108         for (i = 0; i < oldsize; i++)
0109             out[i] = out[i]/N;
0110         break;
0111     case nsl_corr_norm_unbiased:
0112         for (i = 0; i < oldsize; i++) {
0113             size_t norm = i < oldsize/2 ? i+1 : oldsize - i;
0114             out[i] = out[i]/norm;
0115         }
0116         break;
0117     case nsl_corr_norm_coeff: {
0118         double snorm = cblas_dnrm2((int)n, s, 1);
0119         double rnorm = cblas_dnrm2((int)m, r, 1);
0120         for (i = 0; i < oldsize; i++)
0121             out[i] = out[i]/snorm/rnorm;
0122         break;
0123     }
0124     }
0125 
0126     // reverse array for circular type
0127     if (type == nsl_corr_type_circular) {
0128         for (i = 0; i < N/2; i++) {
0129             double tmp = out[i];
0130             out[i] = out[N - i - 1];
0131             out[N -i - 1] = tmp;
0132         }
0133     }
0134 
0135     return status;
0136 }
0137 
0138 #ifdef HAVE_FFTW3
0139 int nsl_corr_fft_FFTW(double s[], double r[], size_t n, double out[]) {
0140     if (n <= 0)
0141         return -1;
0142 
0143     const size_t size = 2*(n/2+1);
0144     double* in = (double*)malloc(size*sizeof(double));
0145     fftw_plan rpf = fftw_plan_dft_r2c_1d((int)n, in, (fftw_complex*)in, FFTW_ESTIMATE);
0146 
0147     fftw_execute_dft_r2c(rpf, s, (fftw_complex*)s);
0148     fftw_execute_dft_r2c(rpf, r, (fftw_complex*)r);
0149     fftw_destroy_plan(rpf);
0150 
0151     size_t i;
0152 
0153     // multiply
0154     for (i = 0; i < size; i += 2) {
0155         double re = s[i]*r[i] + s[i+1]*r[i+1];
0156         double im = s[i+1]*r[i] - s[i]*r[i+1];
0157 
0158         s[i] = re;
0159         s[i+1] = im;
0160     }
0161 
0162     // back transform
0163     double* o = (double*)malloc(size*sizeof(double));
0164     fftw_plan rpb = fftw_plan_dft_c2r_1d((int)n, (fftw_complex*)o, o, FFTW_ESTIMATE);
0165     fftw_execute_dft_c2r(rpb, (fftw_complex*)s, s);
0166     fftw_destroy_plan(rpb);
0167 
0168     for (i = 0; i < n; i++)
0169         out[i] = s[i]/n;
0170 
0171     free(in);
0172     free(o);
0173     return 0;
0174 }
0175 #endif
0176 
0177 int nsl_corr_fft_GSL(double s[], double r[], size_t n, double out[]) {
0178     gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(n);
0179     gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(n);
0180 
0181     /* FFT s and r */
0182     gsl_fft_real_transform(s, 1, n, real, work);
0183     gsl_fft_real_transform(r, 1, n, real, work);
0184     gsl_fft_real_wavetable_free(real);
0185 
0186     size_t i;
0187     /* calculate halfcomplex product */
0188     out[0] = s[0]*r[0];
0189     for (i = 1; i < n; i++) {
0190         if (i%2) { /* Re */
0191             out[i] = s[i]*r[i];
0192             if (i < n-1) /* when n is even last value is real */
0193                 out[i] += s[i+1]*r[i+1];
0194         } else  /* Im */
0195             out[i] = s[i]*r[i-1] - s[i-1]*r[i];
0196     }
0197 
0198     /* back transform */
0199     gsl_fft_halfcomplex_wavetable *hc = gsl_fft_halfcomplex_wavetable_alloc(n);
0200     gsl_fft_halfcomplex_inverse(out, 1, n, hc, work);
0201     gsl_fft_halfcomplex_wavetable_free(hc);
0202     gsl_fft_real_workspace_free(work);
0203 
0204     return 0;
0205 }