From 0e961c4888c312e9f2ed4608cc36d3996e799b2f Mon Sep 17 00:00:00 2001 From: mcrucifix <michel.crucifix@uclouvain.be> Date: Tue, 8 Oct 2024 20:37:18 +0200 Subject: [PATCH] golden search in C --- NAMESPACE | 1 + R/mfft_real.R | 74 +++++++++++++++++++++++----------- man/mfft.Rd | 6 +-- src/fmft.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 160 insertions(+), 28 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 64c9449..d9c5d53 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -16,6 +16,7 @@ export(mem) export(mfft_anova) export(mfft_complex) export(mfft_real) +export(mfft_real_C) export(periodogram) export(powerspectrum.wavelet) export(reconstruct_mfft) diff --git a/R/mfft_real.R b/R/mfft_real.R index aaeac6a..6353354 100644 --- a/R/mfft_real.R +++ b/R/mfft_real.R @@ -1,7 +1,9 @@ -Q <- function(wT) {sin(wT)/(wT)*(pi^2)/(pi^2-(wT)^2)} -Qprime <- function(y) {ifelse(y==0, 0, pi^2/(pi^2-y^2)/y*(cos(y)+(sin(y)/y)*(3*y^2-pi^2)/(pi^2-y^2)))} -Qsecond0 <- 2/pi^2 - 1./3. +pisquare <- pi^2 + +Q <- function(wT) {sin(wT)/(wT)*(pisquare)/(pisquare-(wT*wT))} +Qprime <- function(y) {ifelse(y==0, 0, pisquare/(pisquare-y*y)/y*(cos(y)+(sin(y)/y)*(3*y*y-pisquare)/(pisquare-y*y)))} +Qsecond0 <- 2/pisquare - 1./3. mfft_analyse <- function(xdata, nfreq, fast = TRUE, nu = NULL, minfreq=NULL, maxfreq=NULL){ @@ -63,27 +65,48 @@ mfft_analyse <- function(xdata, nfreq, fast = TRUE, nu = NULL, minfreq=NULL, max # or either they were not provided, but we are in this case where it was set in "m-1" because # we identified a non-null frequency # in both configurations, we look at a frequency with the fourier transform - fbase <- freqs[which.max(power(hx))] - brackets <- c(fbase-pi/N, fbase+pi/N); + fbase <- freqs[which.max(power(hx))] + brackets <- c(fbase-pi/N, fbase+pi/N); # and then further corrects the brackets if minfreq and maxfreq where provided - brackets[1] <- max(minfreq, brackets[1]) - brackets[2] <- max(maxfreq, brackets[1]) - thx <- t(hx) + brackets[1] <- max(minfreq, brackets[1]) + brackets[2] <- min(maxfreq, brackets[2]) + thx <- t(hx) # after profiling, the fastest seems the first option below # this is the weak link # coding the function in c might be an option - fmax = cmna::goldsectmax(function(f) - { - ft <- f*t - (thx %*% cos(f*t))^2 + (thx %*% sin(f*t))^2}, - brackets[1], brackets[2], tol=1.e-10, m=9999) - - # fmax = cmna::goldsectmax(function(f) { - # ft <- f*t - # (sum(hx * cos(ft)))^2 + (sum(hx %*% sin(ft)))^2}, - # brackets[1], brackets[2], tol=1.e-10, m=9999) + # + tomax <- function(t) { + function(f) { + ft <- f*t + a <- hx %*% cbind(cos(ft), sin(ft)) + a[1]*a[1] + a[2]*a[2] + } + } + + +# print ("x[[m]][1,2]") +# print (as.double(x[[m]][1:2])) + + localxdata <- as.double(x[[m]]) +# print ('call it') +# OUT <- .C("fastgsec", as.double(minfreq), as.double(maxfreq), +# as.integer(N), localxdata , as.double(rep(0,N)), +# outfreq = 0., DUP=TRUE) +# print ('called it') + +# plot (Mod(fft(x[[m]])[0:(N/2)])^2 , type='l') + + fmax = cmna::goldsectmax(tomax(t), + brackets[1], brackets[2], + tol=1.e-10, m=9999) + + + # fmax = cmna::goldsectmax(function(f) { + # ft <- f*t + # (sum(hx * cos(ft)))^2 + (sum(hx %*% sin(ft)))^2}, + # brackets[1], brackets[2], tol=1.e-10, m=9999) # fmax = cmna::goldsectmax(function(f) { # ft <- f*t @@ -91,11 +114,15 @@ mfft_analyse <- function(xdata, nfreq, fast = TRUE, nu = NULL, minfreq=NULL, max # brackets[1], brackets[2], tol=1.e-10, m=9999) + #print (sprintf("fmaxlocal: %.2f ; fmax with C: %.2f", + # fmax, OUT$outfreq)) + +# fmax <- OUT$outfreq if (fmax > freqs[2]/2){ # if we really identified a frequency # we are in this case where the frequency was not a priori provided - # we se set this phase to zero, and the next one to pi/2, and also set the frequencies accordinly + # we se set this phase to zero, and the next one to pi/2, and also set the frequencies accordingly phase[m] <- 0. nu[m] <- fmax phase[m+1] <- pi/2. @@ -190,13 +217,14 @@ mfft_analyse <- function(xdata, nfreq, fast = TRUE, nu = NULL, minfreq=NULL, max S[m] = 0. for (j in seq(m)) S[m] = S[m] + Prod[j]*A[m,j] + # for (j in seq(m)) S[m] = S[m] + A[m,j] * hprod(x[[1]], f[[j]]) # les Sm sont les projections dans la nouvelle base # not necessary, for verification only # computes the B and verify they are orthonormal - B[[m]]=0 - for (j in seq(m)) B[[m]] = B[[m]] + A[m,j] * f[[j]] + # B[[m]]=0 + # for (j in seq(m)) B[[m]] = B[[m]] + A[m,j] * f[[j]] #print ('S[m] computed two different ways') @@ -217,8 +245,8 @@ mfft_analyse <- function(xdata, nfreq, fast = TRUE, nu = NULL, minfreq=NULL, max x[[m+1]] = x[[m]] - # for (j in seq(m)) x[[m+1]] = x[[m+1]] - S[m] * A[m,j]*f[[j]] - x[[m+1]] = x[[m+1]] - S[m] * B[[m]] + for (j in seq(m)) x[[m+1]] = x[[m+1]] - S[m] * A[m,j]*f[[j]] + #x[[m+1]] = x[[m+1]] - S[m] * B[[m]] # print('residu final') # print(m) # print (x[[m+1]][seq(20)]) diff --git a/man/mfft.Rd b/man/mfft.Rd index b50a031..7fc6a0c 100644 --- a/man/mfft.Rd +++ b/man/mfft.Rd @@ -4,12 +4,14 @@ \alias{mfft} \title{Modified Fourier transform} \usage{ -mfft(xdata, minfreq = NULL, maxfreq = NULL, correction = 1, nfreq = 30) +mfft(xdata, nfreq = 30, minfreq = NULL, maxfreq = NULL, correction = 1) } \arguments{ \item{xdata}{The data provided either as a time series (advised), or as a vector. may be complex} +\item{nfreq}{is the number of frequencies returned, must be smaller that the length of xdata.} + \item{minfreq, maxfreq}{If provided, bracket the frequencies to be probed. Note this are more precisely angular velocities (2\pi / period), expressed in time-inverse units with the time resolution encoded in `xdata` if the latter is a time series. @@ -21,8 +23,6 @@ The parameter is passed on to `mfft_real` or `mfft_complex` depending on quite the same for both. This needs to be fixed. one approach would be to define scalars like 'fft', 'mfft', 'mfft_linear', 'mfft_second_order_correction'.} - -\item{nfreq}{is the number of frequencies returned, must be smaller that the length of xdata.} } \value{ a `mfft_deco` object, based on a data.frame with columns "Freq", "Ampl" and "Phases". diff --git a/src/fmft.c b/src/fmft.c index 84bef58..0b773e2 100644 --- a/src/fmft.c +++ b/src/fmft.c @@ -681,7 +681,7 @@ double phisqr(double freq, double xdata[], double ydata[], size_t ndata) xphi = yphi = 0; phifun(&xphi, &yphi, freq, xdata, ydata, ndata); - + // printf ("xphi etc %.f %.f %.f %.f \n', xphi, yphi, xphi*xphi + yphi*yphi"); // return xphi*xphi + yphi*yphi; } @@ -1337,9 +1337,11 @@ double bracket_real(float *powsd, size_t ndata) maxj = 0; maxpow = 0; +/* printf("I am calling bracket real %i \n", ndata);*/ /* probe positive frequencies in priority */ for(j=2;j<=ndata/2-2;j++) +/* printf ("powsd[%d] %.g \n ", j, powsd[j]);*/ if(powsd[j] > powsd[j-1] && powsd[j] > powsd[j+1]) if(powsd[j] > maxpow){ maxj = j; @@ -1356,7 +1358,7 @@ double bracket_real(float *powsd, size_t ndata) if(maxpow == 0) printf("DFT has no maximum ..."); freq = (maxj-1); - printf("freq = %i \n",freq); +/* printf("freq = %d \n",freq);*/ /* if(maxj < ndata/2 - 1) freq = -(maxj-1); */ /* if(maxj > ndata/2 - 1) freq = -(maxj-ndata-1);*/ return (TWOPI*freq / ndata); @@ -1505,3 +1507,104 @@ free_dvector(xdata2,1,n); } +int fastgsec(double *localminfreq, double *localmaxfreq, + int *localndata, double *localxdata, double *localydata, double *outfreq); + +int fastgsec(double *localminfreq, double *localmaxfreq, + int *localndata, double *localxdata, double *localydata, + double *outfreq) +{ + int nearfreqflag; + long j; + float *powsd; + double *xdata, *ydata, *x, *y; + double centerf, leftf, rightf; + double f, A, psi; + + FILE *fp; + + double minfreq = *localminfreq; + double maxfreq = *localmaxfreq; + size_t ndata = *localndata; + + bool fastflag; + + fastflag = isPowerofTwo(ndata) ; + + if (fastflag) +/* (printf("ndata is power of two: we will be faster ! \n"));*/ + if (ndata <= 2){ + printf("at least 2 data needed - output non-reliable"); return(0); + } + +/* printf("prelimarg %d, %zu %d", nfreq, ndata, flag);*/ + + /* ALLOCATION OF VARIABLES */ + +/* xdata = dvector(1,ndata);*/ +/* ydata = dvector(1,ndata);*/ + x = dvector(1,ndata); + y = dvector(1,ndata); + + powsd = vector(1, ndata); + + + xdata = localxdata -1; // -1 because dvector vs *double + ydata = localydata -1; + + /* MULTIPLY THE SIGNAL BY A WINDOW FUNCTION, STORE RESULT IN x AND y */ + window(x, y, xdata, ydata, ndata); + + + /* COMPUTE POWER SPECTRAL DENSITY USING FAST FOURIER TRANSFORM */ + power(powsd, x, y, ndata); + + + /* CHECK IF THE FREQUENCY IS IN THE REQUIRED RANGE */ + while((centerf = bracket_real(powsd, ndata)) < minfreq || centerf > maxfreq) { + + /* IF NO, SUBSTRACT IT FROM THE SIGNAL */ + leftf = centerf - TWOPI / ndata; + rightf = centerf + TWOPI / ndata; + + f = golden(phisqr, leftf, centerf, rightf, x, y, ndata); + + amph(&A, &psi, f, x, y, ndata); + + for(j=1;j<=ndata;j++){ + xdata[j] -= A*cos( f*(j-1) + psi ); + ydata[j] -= A*sin( f*(j-1) + psi ); + } + + window(x, y, xdata, ydata, ndata); + + power(powsd, x, y, ndata); + } + + + leftf = centerf - TWOPI / ndata; + rightf = centerf + TWOPI / ndata; + + /* DETERMINE THE FIRST FREQUENCY */ + f = golden(phisqr, leftf, centerf, rightf, x, y, ndata); + + *outfreq = f ; + + /* COMPUTE AMPLITUDE AND PHASE */ + amph(&A, &psi, f, x, y, ndata); + + /* SUBSTRACT THE FIRST HARMONIC FROM THE SIGNAL */ + for(j=1;j<=ndata;j++){ + xdata[j] -= A*cos( f*(j-1) + psi ); + } + + free_dvector(x, 1, ndata); + free_dvector(y, 1, ndata); + free_vector(powsd, 1, ndata); + + + return 1; +} + + + -- GitLab