ROOT FFTW Wrapper
SineSubtract.h
1 #ifndef _SINE_SUBTRACT_H
2 #define _SINE_SUBTRACT_H
3 
4 #ifdef DONT_HAVE_MINUIT2
5 #warning "Need Minuit2 for SineSubtract."
6 #else
7 
8 
9 /**************************************************************************************
10  * \class FFTtools::SineSubtract
11  *
12  * \brief This class contains an independent implementation of the sine
13  * subtraction CW removal algorithm, suggested by Andres/ Steph
14  * (ANITA Elog 621).
15  *
16  * Cosmin Deaconu <cozzyd@kicp.uchicago.edu> is responsible for this mess.
17  *
18  * This, SineSubtract, is the controlling class. Related classes are
19  * SineFitter, which does the minimization, SineFitter::SineFitFn, which
20  * provides the objective function and its derivative, SineFitterLimits, which
21  * encodes parameter limits, and SineSubtractResult, which stores stuff.
22  *
23  * Many options are available, some of which may even be documented. For
24  * example, the part of the waveform used to train sine subtraction may be
25  * limited (e.g. if you wanted to just use the non-trigger portion). Multiple
26  * waveforms may be fit for at once (a single frequency is fit for in each
27  * iteration, with the amplitude and phase allowed to vary per waveform). It is
28  * also possible to enable storage of intermediate graphs and power spectra at
29  * each step. This is useful for diagnostics and making pretty plots, but
30  * incurs some overhead, so is not recommended for production use.
31  * Gratuitously, there are methods to generate plots and even Beamer slides.
32  *
33  * A sketch of the algorithm is:
34  *
35  * - Rectify waveform and estimate power spectrum (according to the power spectrum option).
36  *
37  * - Find peak, fmax, in power spectrum by finding max magnitude/(1+nfails)^nfails_exp) ,
38  * according to the peak finding option. See below for meaning of nfails.
39  *
40  * - Find sine ( A (sin wt + phi)) that when subtracted minimizes "power"
41  * (avg(v_i^2)); initialize with w = 2pi fmax, A = mag, phase from fft.
42  * Uninterpolated graph is used for this!
43  *
44  * - If power reduced by more than min_power_reduction, subtract
45  *
46  * - If power not reduced by more than min_power reduction, increment nfails
47  * for that bin
48  *
49  * - Repeat until max_iter_without_reduction iterations with not enough
50  * improvement
51  *
52  *
53  * ---
54  *
55  * A lot of time has been spent profiling and trying to make this run as fast
56  * as possible. This results in code that is more difficult to understand than
57  * a naive implementation would allow. Any suggestions for performance
58  * enhancements are awelcome.
59  *
60  * If ENABLE_VECTORIZE is defined, custom vectorized code (using Agner Fog's
61  * VCL, http://www.agner.org/optimize/#vectorclass) is used for the objective
62  * function and gradient. At least until compilers get smart enough to
63  * autovectorize stuff better, this is substantially faster, but obviously
64  * only makes sense in a processor with SIMD instructions. 256-bit floating
65  * point registers are used (e.g. AVX) but the VCL will gracefully fallback to
66  * 128-bit registers at compile time should your processor be too old. At some
67  * point AVX-512 will appear, so that might require a few changes.
68  *
69  * **WARNING**: You will not get the same "answers" in vectorized mode. This
70  * is expected, due to usage of different math functions and the general
71  * non-associativity of of floating point math.
72  *
73  * ---
74  *
75  * A number of additional compile-time options influence the behavior:
76  *
77  * - If SINE_SUBTRACT_USE_FLOATS is defined, the fitter will use floats
78  * instead of doubles. In principle, this should be twice the vectorization,
79 * but in practice, the overhead of copying and casting seems to be
80 * problematic.
81  *
82  * - If SINE_SUBTRACT_FORCE_ALIGNED is defined, the fitter will copy the
83  * doubles into an aligned array. This might make a performance difference on
84  * old processors, but seems to not help for the most part.
85  *
86  * - If SINE_SUBTRACT_PROFILE is defined, the time spent calling subtractCW is
87  * printed.
88  *
89  * - if SINE_SUBTRACT_DONT_HORIZONTAL_ADD is defined, horizontal addition is
90  * disabled. You probably don't want to do this, I added this when I was
91  * investigating some strange numerical behavior.
92  *
93  * See macros/testArtificialSubtract.C to see SineSubtract in action.
94  *
95  * ---
96  *
97  * TODO list:
98  *
99  * - Better tuning of default step sizes / limits ?
100  *
101  * - Minuit2 adds quite a bit of overhead, and is one of the things preventing
102  * this from running even faster
103  *
104  ********************************************************************************************/
105 
106 #include "Minuit2/Minuit2Minimizer.h"
107 #include "FFTtools.h"
108 #include "TObject.h"
109 
110 #include <vector>
111 
112 class TGraph;
113 class TCanvas;
114 
115 namespace FFTtools
116 {
117 
133  {
134  public:
135 
138 
141 
144 
147 
150 
153 
154 
155 
156  SineFitterLimits(double maxA_relative_to_guess_ = 4, double minA_relative_to_guess_ = 0.25,
157  double max_n_df_relative_to_guess_ = 1, double phase_start_error_ = TMath::Pi() / 16,
158  double amp_start_error_ = 0.1, double freq_start_error_ = 0.1) :
159  maxA_relative_to_guess(maxA_relative_to_guess_),
160  minA_relative_to_guess(minA_relative_to_guess_),
161  max_n_df_relative_to_guess(max_n_df_relative_to_guess_) ,
162  phase_start_error(phase_start_error_) ,
163  amp_start_error(amp_start_error_) ,
164  freq_start_error(freq_start_error_)
165  {;}
166 
167 
168 
169  };
170 
176  {
177 
178  public:
180  SineFitter();
181 
183  virtual ~SineFitter();
184 
191  void setGuess(double f, int ntrace, const double *ph, double amp);
192 
202  void doFit(int ntrace, const int * nsamples, const double ** x, const double **y, const double * w = 0, const double ** env = 0);
203 
205  double getFreq() const {return freq;}
206 
208  const double* getPhase() const {return &phase[0];}
209 
211  const double* getAmp() const {return &amp[0];}
212 
214  double getFreqErr() const {return freq_err;}
215 
217  const double *getPhaseErr() const {return &phase_err[0];}
218 
220  const double *getAmpErr() const {return &amp_err[0];}
221 
223  double getPower() const {return min.MinValue(); }
224 
226  void setVerbose(bool v) { verbose=v; }
227 
229  int getStatus() const { return min.Status(); }
230 
232  void setLimitOptions(const SineFitterLimits * lims) { limits = *lims; }
233 
234  ROOT::Minuit2::Minuit2Minimizer * minimizer() { return &min; }
235 
245  TGraph* getEvalRecordGraph(int i = -1){
246  i = i < 0 ? grEvalRecords.size() - 1 : i;
247  if(i >= 0 && i < grEvalRecords.size()){
248  return grEvalRecords[i];
249  }
250  return NULL;
251  }
252  size_t nEvalRecords(){return grEvalRecords.size();}
253  void SetDoEvalRecord(bool doEvalRecord){fDoEvalRecord = doEvalRecord;}
254  Bool_t GetDoEvalRecord() const {return fDoEvalRecord;}
255  void deleteEvalRecords();
256 
257  private:
258  ROOT::Minuit2::Minuit2Minimizer min;
259  bool verbose;
260  double eval(const double *p);
261  double freq;
262  std::vector<double> phase;
263  std::vector<double> amp;
264  double freq_err;
265  std::vector<double> phase_err;
266  std::vector<double> amp_err;
267  bool fDoEvalRecord;
268  std::vector<TGraph*> grEvalRecords;
269 
270  double freq_factor;
271  double min_amp_mean, max_amp_mean;
272  double min_amp_guess, max_amp_guess;
273  SineFitterLimits limits;
274 
275 
276 
277 #ifndef __CINT__ //CINT is stupid about subclasses
278 
284  class SineFitFn : public ROOT::Math::IGradientFunctionMultiDim
285  {
286  public:
287 
289  SineFitFn(SineFitter* parent = NULL);
290 
292  virtual ~SineFitFn();
293 
303  void setXY(int ntraces, const int * nsamples, const double **xx, const double **yy, const double * w = 0, const double ** env = 0);
304 
306  virtual double DoEval(const double *p) const;
307 
309  virtual double DoDerivative(const double *p, unsigned int coord) const;
310 
312  unsigned NDim() const { return 1+2*nt; }
313 
315  virtual ROOT::Math::IBaseFunctionMultiDim * Clone() const;
316 
317  private:
318  SineFitter* fContainer; // pointer to parent
319 
320  int *ns;
321  int nt;
322 #ifdef SINE_SUBTRACT_USE_FLOATS
323  float **x;
324  float **y;
325  float **env;
326  const double **xp, **yp, ***envp;
327 #elif defined(SINE_SUBTRACT_FORCE_ALIGNED)
328  double **x;
329  double **y;
330  double **env;
331  const double **xp, **yp, **envp;
332 #else
333  const double ** x;
334  const double ** y;
335  const double ** env;
336 #endif
337  const double * wgt;
338 
339 
340  } f;
341 #endif
342 
343  };
344 
345  /* This class stores the minimization result. It would be a struct if CINT weren't
346  * stupid about structs */
348  {
349 
350  public:
351 
353  void clear();
354 
356  void append(const SineSubtractResult * r);
357 
359  virtual ~SineSubtractResult(){;}
360 
361 
363  std::vector<double> powers;
364 
366  std::vector<std::vector<double> >phases;
367 
369  std::vector<double> freqs;
370 
372  std::vector<std::vector<double> > amps;
373 
375  std::vector<std::vector<double > > phases_errs;
376 
378  std::vector<double> freqs_errs;
379 
381  std::vector<std::vector<double> >amps_errs;
382 
383 
384  ClassDef(SineSubtractResult,3);
385  };
386 
387 
392  {
393 
394  public:
395 
404  SineSubtract(int max_iter_without_reduction = 3, double min_power_reduction = 0.05, bool store = false);
405 
406 
416  SineSubtract(const TGraph * freq_dependent_min_power_reduction, int max_iter_without_reduction = 3, bool store = false);
417 
418 
420  virtual ~SineSubtract();
421 
422  /* Subtract CW from a single trace. Sine Subtraction can handle both evenly spaced and unevely spaced waveforms.
423  * For evenly spaced waveforms, you should pass dt <= 0, otherwise you should pass the nominal average.
424  *
425  * The input graph is not touched. A new graph is allocated for the subtraction.
426  *
427  * @param g The input TGraph to subtract from
428  * @param dt The nominal sample rate for uneven waveforms. If <=0, then the graph is assumed to be even and dt is computed from the first time step.
429  * @return The subtracted Graph.
430  */
431  TGraph * subtractCW(const TGraph * g, double dt, const SineSubtractResult* result = NULL);
432 
433 
446  void subtractCW(int ng, TGraph ** g, double dt, const double * w = 0, const SineSubtractResult* result = NULL);
447 
453  void setFreqLimits(double min, double max) { fmin.clear(); fmin.push_back(min); fmax.clear(); fmax.push_back(max); }
454 
461  void setFreqLimits(int nbands, const double * min, const double * max) { fmin.clear(); fmax.clear(); for (int i = 0; i < nbands;i++) { fmin.push_back(min[i]); fmax.push_back(max[i]); }; }
462 
464  void unsetFreqLimits() { fmin.clear(); fmax.clear(); high_factor = 1; }
465 
466 
470  void setVerbose(bool v) {verbose = v; fitter.setVerbose(v); }
471 
472 
484  void setTraceLimits(int min, int max) { tmin = min; tmax = max; }
485 
486 
487 
495  {
496  FFT,
498  };
499 
500 
503  {
504  FFT_NPAD,
506  };
507 
510  {
511  LS_OVERSAMPLE_FACTOR,
513  };
514 
515 
518  void setNFailsExponent(double exp = 0.5) { nfail_exponent = exp ; }
519 
520 
524  void setPowerSpectrumEstimator(PowerSpectrumEstimator estimator, const double * params = 0);
525 
526 
533  {
534  GLOBALMAX,
538  };
539 
540 
542  {
543  NF_NEIGHBOR_FACTOR,
545  };
546 
547  /* These are basically based on the
548  * options to TSpectrum::searchHighRes
549  */
551  {
552  TS_SIGMA,
554  TS_NDECONV_ITERATIONS, //default = 3
555  TS_AVERAGE_WINDOW, //default = 3
556  TS_NPARAMS
557  };
558 
560  {
561  SGS_ORDER,
564  };
565 
570  void setPeakFindingOption(PeakFindingOption peak, const double * params = 0);
571 
573  {
574  ENV_NONE,
575  ENV_HILBERT, //fit to hilbert envelope
576  ENV_RMS, //fit to sliding RMS envelope
577  ENV_PEAK //fit to sliding RMS envelope
578  };
579 
580  enum ENV_HILBERT_Params
581  {
582  ENV_HILBERT_FIT_ORDER, //default = 3, if < 0, no fit (just take values)
583  ENV_HILBERT_NPARAMS
584 
585  };
586 
587  enum ENV_RMS_Params
588  {
589  ENV_RMS_FIT_ORDER, //default = 3, if < 0, no fit (just take envelope values)
590  ENV_RMS_WINDOW, //default = 5
591  ENV_RMS_NPARAMS
592  };
593 
594  enum ENV_PEAK_Params
595  {
596  ENV_PEAK_FIT_ORDER, //default = 3, if < 0, no fit (just take envelope values)
597  ENV_PEAK_MINDISTANCE, //default = 5 (appropriate for ~200 MHz min freq)
598  ENV_PEAK_NPARAMS
599  };
600 
604  void setEnvelopeOption(EnvelopeOption env, const double * params = 0);
605 
607  const std::vector<double> * getPowerSequence() const { return &r.powers; }
608 
610  const std::vector<std::vector<TGraph*> > * getStoredGraphs() const { return &gs; }
611 
613  const std::vector<TGraph*> * getStoredSpectra() const { return &spectra; }
614 
615 
617  size_t nStoredGraphsInChannel() const { return gs[0].size(); }
618 
620  size_t nChannels() const { return gs.size(); }
621 
623  TGraph* storedGraph(int i, int c) { return gs[c][i]; }
624  TGraph* storedEnvelope(int i, int c) { return env_gs[c][i]; }
625 
627  size_t nStoredSpectra() const { return spectra.size(); }
628 
630  TGraph* storedSpectra(int i) { return spectra[i]; }
631 
633  int getNSines() const;
634 
635  /* Return the number of traces fit for */
636  int getNGraphs() const { return r.phases.size(); }
637 
639  const double * getPhases(int g) const { return &r.phases[g][0]; }
640 
642  const double * getFreqs() const { return &r.freqs[0]; }
643 
645  const double * getAmps(int g) const { return &r.amps[g][0]; }
646 
648  const double * getPhaseErrs(int g) const { return &r.phases_errs[g][0]; }
649 
651  const double * getFreqErrs() const { return &r.freqs_errs[0]; }
652 
654  const double * getAmpErrs(int g) const { return &r.amps_errs[g][0]; }
655 
657  const SineSubtractResult * getResult() const { return &r; }
658 
659  /* Set the maximum number of failed iterations before giving up. */
660  void setMaxIterationsWithoutReduction(int max) { maxiter = max; }
661 
663  void setAbsoluteMaxIterations(int max) { abs_maxiter = max; }
664 
666  void setMaxSuccessfulIterations(int max) { max_successful_iter = max; }
667 
669  void setMinPowerReduction(double min) {min_power_reduction = min; g_min_power = 0;};
670 
671  /* Set the frequency-dependent threshold before giving up. */
672  void setMinPowerReduction(const TGraph *g) { g_min_power= g; }
673 
674 
676  void setStore(bool s) { store = s; }
677 
679  void setFitterLimitOptions( const SineFitterLimits * lims) { fitter.setLimitOptions(lims); }
680 
681  //these are super auxilliary and could be moved out (they only need public methods anyway)
682 
688  void makePlots(TCanvas * cpower = 0,TCanvas *cspectra = 0, int ncols = 4) const;
689 
699  void makeSlides(const char * title = "SineSubtract", const char * prefix = "sinsub", const char * outdir = ".", const char* format = "png", bool standalone = true) const;
700 
701  SineFitter * sineFitter() { return &fitter; }
702 
703  private:
704  int abs_maxiter;
705  int maxiter;
706  double nfail_exponent;
707  int max_successful_iter;
708  int tmin, tmax;
709  std::vector<double> fmin;
710  std::vector<double> fmax;
711  double min_power_reduction;
712  std::vector<std::vector<TGraph*> > gs;
713  std::vector<std::vector<TGraph*> > env_gs;
714  std::vector<TGraph*> spectra;
715  SineSubtractResult r;
716  bool store;
717  double high_factor;
718 
719  PowerSpectrumEstimator power_estimator;
720  double * power_estimator_params;
721  PeakFindingOption peak_option;
722  double * peak_option_params;
723 
724 
725  EnvelopeOption envelope_option;
726  double * envelope_option_params;
727 
728  const TGraph * g_min_power;
729 
730  bool verbose;
731  void reset();
732  unsigned id;
733 
735  int findMaxFreq(int Nfreq, const double * freq, const double * mag, const int * nfails) const;
736  bool allowedFreq(double f, double df) const;
737 
738  SineFitter fitter;
739  };
740 
741 }
742 
743 #endif
744 #endif
const double * getPhases(int g) const
Definition: SineSubtract.h:639
void setStore(bool s)
Definition: SineSubtract.h:676
const double * getPhaseErrs(int g) const
Definition: SineSubtract.h:648
std::vector< std::vector< double > > amps_errs
Definition: SineSubtract.h:381
Definition: SineSubtract.h:391
TGraph * storedSpectra(int i)
Definition: SineSubtract.h:630
std::vector< double > powers
Definition: SineSubtract.h:363
int getNSines() const
Definition: SineSubtract.cxx:1365
void makeSlides(const char *title="SineSubtract", const char *prefix="sinsub", const char *outdir=".", const char *format="png", bool standalone=true) const
Definition: SineSubtract.cxx:1370
EnvelopeOption
Definition: SineSubtract.h:572
void setGuess(double f, int ntrace, const double *ph, double amp)
Definition: SineSubtract.cxx:177
const double * getPhase() const
Definition: SineSubtract.h:208
size_t nChannels() const
Definition: SineSubtract.h:620
const double * getAmpErrs(int g) const
Definition: SineSubtract.h:654
std::vector< std::vector< double > > phases
Definition: SineSubtract.h:366
const double * getAmp() const
Definition: SineSubtract.h:211
void setVerbose(bool v)
Definition: SineSubtract.h:470
virtual ~SineSubtractResult()
Definition: SineSubtract.h:359
void setNFailsExponent(double exp=0.5)
Definition: SineSubtract.h:518
Definition: AnalyticSignal.h:10
PeakFindingOption
Definition: SineSubtract.h:532
double getPower() const
Definition: SineSubtract.h:223
int getStatus() const
Definition: SineSubtract.h:229
TGraph * getEvalRecordGraph(int i=-1)
Definition: SineSubtract.h:245
virtual ~SineFitter()
Definition: SineSubtract.cxx:171
double phase_start_error
Definition: SineSubtract.h:146
Definition: SineSubtract.h:132
default = 0.15
Definition: SineSubtract.h:544
void setVerbose(bool v)
Definition: SineSubtract.h:226
void setTraceLimits(int min, int max)
Definition: SineSubtract.h:484
SineFitter()
Definition: SineSubtract.cxx:153
void setFitterLimitOptions(const SineFitterLimits *lims)
Definition: SineSubtract.h:679
TGraph * storedGraph(int i, int c)
Definition: SineSubtract.h:623
Definition: SineSubtract.h:347
std::vector< double > freqs
Definition: SineSubtract.h:369
const double * getAmps(int g) const
Definition: SineSubtract.h:645
std::vector< double > freqs_errs
Definition: SineSubtract.h:378
const SineSubtractResult * getResult() const
Definition: SineSubtract.h:657
virtual ~SineSubtract()
Definition: SineSubtract.cxx:1516
Definition: SineSubtract.h:175
void setAbsoluteMaxIterations(int max)
Definition: SineSubtract.h:663
SineSubtract(int max_iter_without_reduction=3, double min_power_reduction=0.05, bool store=false)
Definition: SineSubtract.cxx:858
NEIGHBORFACTOR_Params
Definition: SineSubtract.h:541
size_t nStoredSpectra() const
Definition: SineSubtract.h:627
void setEnvelopeOption(EnvelopeOption env, const double *params=0)
Definition: SineSubtract.cxx:1736
double getFreqErr() const
Definition: SineSubtract.h:214
void setLimitOptions(const SineFitterLimits *lims)
Definition: SineSubtract.h:232
default = 0.05
Definition: SineSubtract.h:554
std::vector< std::vector< double > > phases_errs
Definition: SineSubtract.h:375
const double * getFreqErrs() const
Definition: SineSubtract.h:651
const std::vector< TGraph * > * getStoredSpectra() const
Definition: SineSubtract.h:613
void setPowerSpectrumEstimator(PowerSpectrumEstimator estimator, const double *params=0)
Definition: SineSubtract.cxx:1677
(default) Compute power spectrum using magnigude of FFT, see FFT_Param enum for parameter names and d...
Definition: SineSubtract.h:497
double minA_relative_to_guess
Definition: SineSubtract.h:140
default =2
Definition: SineSubtract.h:512
std::vector< std::vector< double > > amps
Definition: SineSubtract.h:372
const double * getAmpErr() const
Definition: SineSubtract.h:220
const std::vector< double > * getPowerSequence() const
Definition: SineSubtract.h:607
void setMaxSuccessfulIterations(int max)
Definition: SineSubtract.h:666
void setMinPowerReduction(double min)
Definition: SineSubtract.h:669
(default) requires being at least neighbor_factor bigger than one of the neighbors. See NEIGHBORFACTOR_Params for parametser
Definition: SineSubtract.h:536
void setPeakFindingOption(PeakFindingOption peak, const double *params=0)
Definition: SineSubtract.cxx:1705
default = 2
Definition: SineSubtract.h:553
void doFit(int ntrace, const int *nsamples, const double **x, const double **y, const double *w=0, const double **env=0)
Definition: SineSubtract.cxx:722
default = 5 , really halfwidth, (treated as int)
Definition: SineSubtract.h:563
No envelope is used.
Definition: SineSubtract.h:575
const double * getPhaseErr() const
Definition: SineSubtract.h:217
const double * getFreqs() const
Definition: SineSubtract.h:642
Use TSpectrum (probably super slow). See TSPECTRUM_Params for parameters.
Definition: SineSubtract.h:537
const std::vector< std::vector< TGraph * > > * getStoredGraphs() const
Definition: SineSubtract.h:610
void clear()
Definition: SineSubtract.cxx:896
double getFreq() const
Definition: SineSubtract.h:205
void makePlots(TCanvas *cpower=0, TCanvas *cspectra=0, int ncols=4) const
Definition: SineSubtract.cxx:1480
void unsetFreqLimits()
Definition: SineSubtract.h:464
PowerSpectrumEstimator
Definition: SineSubtract.h:494
double freq_start_error
Definition: SineSubtract.h:152
default = 3 (treated as int)
Definition: SineSubtract.h:562
void append(const SineSubtractResult *r)
Definition: SineSubtract.cxx:880
void setFreqLimits(int nbands, const double *min, const double *max)
Definition: SineSubtract.h:461
default =0
Definition: SineSubtract.h:505
double maxA_relative_to_guess
Definition: SineSubtract.h:137
SAVGOLSUB_Params
Definition: SineSubtract.h:559
void setFreqLimits(double min, double max)
Definition: SineSubtract.h:453
TSPECTRUM_Params
Definition: SineSubtract.h:550
double max_n_df_relative_to_guess
Definition: SineSubtract.h:143
double amp_start_error
Definition: SineSubtract.h:149
size_t nStoredGraphsInChannel() const
Definition: SineSubtract.h:617
LOMBSCARGLE_Params
Definition: SineSubtract.h:509
FFT_Params
Definition: SineSubtract.h:502
Just uses the global maximum (you probably don&#39;t want this) . No params.
Definition: SineSubtract.h:535