changeset 10455:34b4175ba644 octave-forge

Fixed some variables in lombnormcoeff, added fastlsreal.
author benjf5
date Tue, 19 Jun 2012 20:23:47 +0000
parents 52962c2ee902
children 6d2550ecf907
files extra/lssa/fastlsreal.cc extra/lssa/lombnormcoeff.m
diffstat 2 files changed, 345 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/extra/lssa/fastlsreal.cc	Tue Jun 19 20:23:47 2012 +0000
@@ -0,0 +1,320 @@
+/* Copyright (C) 2012 Benjamin Lewis <benjf5@gmail.com>
+ * Licensed under the GNU GPLv2
+ */
+
+
+#include <octave/oct.h>
+#include <octave/unwind-prot.h>
+#include <complex>
+#include <string>
+#include <math.h>
+#include <iostream>
+#include <exception>
+
+ComplexRowVector flsreal( RowVector tvec , ComplexRowVector xvec ,
+			     double maxfreq , int octaves , int coefficients);
+
+
+DEFUN_DLD(fastlsreal,args,nargout, "fastlsreal(time,magnitude,maximum_frequency,octaves,coefficients)") {
+  if ( args.length() != 5 ) {
+    print_usage();
+    return octave_value_list ();
+  }
+  RowVector tvals = args(0).row_vector_value();
+  ComplexRowVector xvals = args(1).complex_row_vector_value();
+  double omegamax = args(2).double_value();
+  int noctaves = args(3).int_value();
+  int ncoeff = args(4).int_value();
+  if ( tvals.numel() != xvals.numel() ){
+    if ( tvals.numel() > xvals.numel() ) {
+      error("More time values than magnitude values.");
+    } else {
+      error("More magnitude values than time values.");
+    }
+  }
+  if ( ncoeff == 0 ) error("No coefficients to compute.");
+  if ( noctaves == 0 ) error("No octaves to compute over.");
+  if ( omegamax == 0 ) error("No difference between minimal and maximal frequency.");
+  octave_value_list retval;  
+  if ( !error_state) {
+    ComplexRowVector results = flscomplex(tvals,xvals,omegamax,noctaves,ncoeff);
+    retval(0) = octave_value(results);
+  } else {
+    return octave_value_list ();
+  }
+  return retval;
+
+}
+
+ComplexRowVector flsreal( RowVector tvec , ComplexRowVector xvec ,
+			  double maxfreq, int octaves, int coefficients ) {
+  struct Precomputation_Record {
+    Precomputation_Record *next;
+    std::complex<double> power_series[12]; // I'm using 12 as a matter of compatibility, only.
+    bool stored_data;
+  };
+
+  ComplexRowVector results = ComplexRowVector (coefficients * octaves );
+
+  double tau, delta_tau, tau_0, tau_h, n_inv, mu,
+    omega_oct, omega_multiplier, octavemax, omega_working,
+    loop_tau_0, loop_delta_tau;
+  double length = ( tvec((tvec.numel()-1)) - tvec( octave_idx_type (0)));
+  int octave_iter, coeff_iter;
+  std::complex<double> zeta, z_accumulator, exp_term, exp_multiplier, alpha,
+    iota, i_accumulator;
+  octave_idx_type n = tvec.numel();
+  std::complex<double> temp_array[12];
+  for ( int array_iter = 0 ; array_iter < 12 ; array_iter++ ) {
+    temp_array[array_iter] = std::complex<double> ( 0 , 0 );
+  }
+  int factorial_array[12];
+  factorial_array[0] = 1;
+  for ( int i = 1 ; i < 12 ; i++ ) {
+    factorial_array[i] = factorial_array[i-1] * i;
+  }
+  n_inv = 1.0 / n;
+  mu = (0.5 * M_PI)/length; // Per the article; this is in place to improve numerical accuracy if desired.
+  /* Viz. the paper, in which Dtau = c / omega_max, and c is stated as pi/2 for floating point processors,
+   * In the case of this computation, I'll go by the recommendation.
+   */
+  delta_tau = M_PI / ( 2 * maxfreq );
+  tau_0 = tvec(0) + delta_tau;
+  tau_h = tau_0;
+  size_t precomp_subset_count = (size_t) ceil( ( tvec(tvec.numel()-1) - tvec(0) ) / ( 2 * delta_tau ) );
+  // I've used size_t because it will work for my purposes without threatening undefined behaviour.
+  const std::complex<double> im = std::complex<double> ( 0 , 1 ); //I seriously prefer C99's complex numbers.
+
+  octave_idx_type k ( 0 ); // Iterator for accessing xvec, tvec.
+
+  Precomputation_Record * complex_precomp_records_head, *complex_record_current,
+    *complex_record_tail, *complex_record_ref, *complex_record_next, *iota_precomp_records_head,
+    *iota_record_current, *iota_record_tail, *iota_record_ref, *iota_record_next;
+  complex_record_current = complex_precomp_records_head = new  Precomputation_Record;
+  iota_record_current = iota_precomp_records_head = new Precomputation_Record;
+  for ( size_t p_r_iter = 1 ; p_r_iter < precomp_subset_count ; p_r_iter++ ) {
+    complex_record_current->next = new Precomputation_Record;
+    iota_record_current->next = new Precomputation_Record;
+    complex_record_current = complex_record_current->next;
+    iota_record_current = iota_record_current->next;
+  }
+  complex_record_tail = complex_record_current;
+  iota_record_tail = iota_record_current;
+  complex_record_current = complex_precomp_records_head;
+  iota_record_current = iota_precomp_records_head;
+  complex_record_tail->next = 0;
+  iota_record_tail->next = 0;
+  /* A test needs to be included for if there was a failure, but since
+   * precomp_subset_count is of type size_t, it should be okay. */
+  for( ; complex_record_current != 0 ; complex_record_current = complex_record_current->next ) {
+    for ( int j = 0 ; j < 12 ; j++ ) { 
+      complex_record_current->power_series[j] = std::complex<double> ( 0 , 0 );
+    } // To avoid any trouble down the line, although it is an annoyance.
+    // Error is traced this far. Time to see if it's in this loop.
+    while ( (k < n) && (abs(tvec(k)-tau_h) <= delta_tau) ) {
+      double p;
+      for ( int j = 0 ; j < 12 ; j++ ) {
+	alpha.real() = xvec(k).real();
+	alpha.imag() = xvec(k).imag();
+	if ( !( j % 2 ) ) {
+	  if ( ! ( j % 4 ) ) {
+	    alpha.real() = xvec(k).real() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	    alpha.imag() = xvec(k).imag() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	  } else {
+	    alpha.real() = -1 * xvec(k).real() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	    alpha.imag() = -1 * xvec(k).imag() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	  } 
+	} else {
+	  if ( ! ( j % 3 ) ) {
+	    alpha.real() = -1 * xvec(k).imag() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	    alpha.imag() = -1 * xvec(k).real() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	  } else {
+	    alpha.real() = xvec(k).imag() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	    alpha.imag() = xvec(k).real() * pow(mu,j) * pow(tvec(k)-tau_h,j) / factorial_array[j];
+	  }
+	}
+	complex_record_current->power_series[j].real() += alpha.real();
+	complex_record_current->power_series[j].imag() += alpha.imag();
+      }
+      // Computes each next step of the power series for the given power series element.
+      // j was reused since it was a handy inner-loop variable, even though I used it twice here.
+      complex_record_current->stored_data = true;
+      k++;
+    }
+    tau_h += ( 2 * delta_tau );
+  }
+  // At this point all precomputation records have been
+  // exhausted for complex records. Short-circuit is abused
+  // to avoid overflow errors.
+  // Reset k, tau_h to reset the process. I may rewrite
+  // these loops to be one, since running twice as long to
+  // do the same thing is painful. May also move to a switch
+  // in the previous section too.
+  k = 0;
+  tau_h = tau_0;
+  for( ; iota_record_current != 0 ; iota_record_current = iota_record_current->next ) {
+    for ( int j = 0 ; j < 12 ; j++ ) { 
+      complex_record_current->power_series[j] = std::complex<double> ( 0 , 0 );
+    }
+    while( ( k < n ) && (abs(tvec(k)-tau_h) <= delta_tau) ) {
+      double comps[12];
+      iota_record_current->power_series[0].real() = 1;
+      comps[0] = 1;
+      for ( int j = 1 ; j < 12 ; j++ ) {
+	comps[j] = comps[j-1] * mu * (tvec(k)-tau_h);
+	switch ( j % 4 ) {
+	case 0 :
+	  iota_record_current->power_series[j].real() += comps[j] / factorial_array[j] ;
+	  break;
+	case 1:
+	  iota_record_current->power_series[j].imag() += comps[j] / factorial_array[j] ;
+	  break;
+	case 2:
+	  iota_record_current->power_series[j].real() -= comps[j] / factorial_array[j] ;
+	  break;
+	case 3:
+	  iota_record_current->power_series[j].imag() -= comps[j] / factorial_array[j] ;
+	  break;
+	}
+      }
+      iota_record_current->stored_data = true;
+      k++;
+    }
+    tau_h += ( 2 * delta_tau );
+  }
+      
+  
+  /* Summation of coefficients for each frequency. As we have ncoeffs * noctaves elements,
+   * it makes sense to work from the top down, as we have omega_max by default (maxfreq)
+   */
+
+  omega_oct = maxfreq / mu;
+  omega_multiplier = exp(-log(2)/coefficients);
+  octavemax = maxfreq;
+  loop_tau_0 = tau_0;
+  loop_delta_tau = delta_tau;
+  
+  octave_idx_type iter ( 0 );
+  
+  double real_part = 0, imag_part = 0, real_part_accumulator = 0, imag_part_accumulator = 0;
+
+  // Loops need to first travel over octaves, then coefficients;
+  
+  for ( octave_iter = octaves ; ; omega_oct *= 0.5 , octavemax *= 0.5 , loop_tau_0 += loop_delta_tau , loop_delta_tau *= 2 ) {
+    omega_working = omega_oct;
+    exp_term = std::complex<double> ( cos ( - omega_working * loop_tau_0 ) ,
+				      sin ( - omega_working * loop_tau_0 ) );
+    exp_multiplier = std::complex<double> ( cos ( - 2 * omega_working * loop_delta_tau ) ,
+					    sin ( - 2 * omega_working * loop_delta_tau ) );
+    iota_exp_term = std::complex<double> ( cos ( - 2 * omega_working * loop_tau_0 ) ,
+					   sin ( - 2 * omega_working * loop_tau_0 ) );
+    iota_exp_multiplier = std::complex<double> ( cos ( - 2 * omega_working * loop_delta_tau ) ,
+						 sin ( - 2 * omega_working * loop_delta_tau ) );
+    for ( coeff_iter = 0 ; coeff_iter < coefficients ; coeff_iter++, omega_working *= omega_multiplier){
+      real_part_accumulator = 0;
+      imag_part_accumulator = 0;
+      real_part = 0;
+      imag_part = 0;
+      for ( complex_record_current = complex_precomp_records_head ; complex_record_current ;
+	    complex_record_current = complex_record_current->next, exp_term *= exp_multiplier ) {
+	for ( int array_iter = 0 ; array_iter < 12 ; array_iter++ ) {
+	  z_accumulator = ( pow(omega_working,array_iter) * complex_record_current->power_series[array_iter] );
+	  real_part_accumulator += z_accumulator.real();
+	  imag_part_accumulator += z_accumulator.imag();
+	}
+	real_part = real_part + ( exp_term.real() * real_part_accumulator - ( exp_term.imag() * imag_part_accumulator ) );
+	imag_part = imag_part + ( exp_term.imag() * real_part_accumulator + exp_term.real() * imag_part_accumulator );
+      }
+      for ( iota_record_current = iota_precomp_records_head; iota_record_current ;
+	    iota_record_current = iota_record_current->next, iota_exp_term = iota_exp_term_multiplier ) {
+	
+      }
+      results(iter) = std::complex<double> ( n_inv * real_part , n_inv * imag_part );
+      iter++;
+    }
+    if ( !(--octave_iter) ) break;
+    /* If we've already reached the lowest value, stop.
+     * Otherwise, merge with the next computation range.
+     */
+    double exp_power_series_elements[12];
+    exp_power_series_elements[0] = 1;
+    for ( int r_iter = 1 ; r_iter < 12 ; r_iter++ ) {
+      exp_power_series_elements[r_iter] = exp_power_series_elements[r_iter-1]
+	* ( mu * loop_delta_tau) * ( 1.0 / ( (double) r_iter ) );
+    }
+    try{ 
+      for ( complex_record_current = complex_precomp_records_head ; complex_record_current ;
+	    complex_record_current = complex_record_current->next ) {
+	if ( ! ( complex_record_ref = complex_record_current->next ) || ! complex_record_ref->stored_data ) {
+	  if ( complex_record_current->stored_data ) {
+	    std::complex<double> temp[12];
+	    for( int array_init = 0 ; array_init < 12 ; array_init++ ) { temp[array_init] = std::complex<double>(0,0); }
+	    for( int p = 0 ; p < 12 ; p ++ ) {
+	      double step_floor_r = floor( ( (double) p ) / 2.0 );
+	      double step_floor_i = floor( ( (double) ( p - 1 ) ) / 2.0 );
+	      for( int q = 0 ; q < step_floor_r ; q++ ) {
+		temp[p] += exp_power_series_elements[2*q] * pow((double)-1,q) * complex_record_current->power_series[p - ( 2 * q )];
+	      }
+	      for( int q = 0 ; q <= step_floor_i ; q++ ) {
+		temp[p] += im * exp_power_series_elements[2 * q + 1] * pow((double)-1,q) * complex_record_current->power_series[p - ( 2 * q ) - 1];
+	      }
+	    }
+	    for ( int array_iter = 0 ; array_iter < 12 ; array_iter++ ) {
+	      complex_record_current->power_series[array_iter].real() = temp[array_iter].real();
+	      complex_record_current->power_series[array_iter].imag() = temp[array_iter].imag();
+	    }
+	    if ( ! complex_record_ref ) break; // Last record was reached
+	  }
+	  else {
+	    complex_record_next = complex_record_ref;
+	    if ( complex_record_current->stored_data ) {
+	      std::complex<double> temp[12];
+	      for( int array_init = 0 ; array_init < 12 ; array_init++ ) { temp[array_init] = std::complex<double>(0,0); }
+	      for( int p = 0 ; p < 12 ; p ++ ) {
+		double step_floor_r = floor( ( (double) p ) / 2.0 );
+		double step_floor_i = floor( ( (double) ( p - 1 ) ) / 2.0 );
+		for( int q = 0 ; q < step_floor_r ; q++ ) {
+		  temp[p] += exp_power_series_elements[2*q] * pow((double)-1,q) * ( complex_record_current->power_series[p - ( 2 * q )] - complex_record_next->power_series[p - (2*q)] );
+		}
+		for( int q = 0 ; q <= step_floor_i ; q++ ) {
+		  temp[p] += im * exp_power_series_elements[2 * q + 1] * pow((double)-1,q) * ( complex_record_current->power_series[p - ( 2 * q ) - 1] - complex_record_next->power_series[p - ( 2 * q ) - 1 ] );
+		}
+	      }
+	      for ( int array_iter = 0 ; array_iter < 12 ; array_iter++ ) {
+		complex_record_current->power_series[array_iter].real() = temp[array_iter].real();
+		complex_record_current->power_series[array_iter].imag() = temp[array_iter].imag();
+	      }
+	    } else {
+	      std::complex<double> temp[12];
+	      for( int array_init = 0 ; array_init < 12 ; array_init++ ) { temp[array_init] = std::complex<double>(0,0); }
+	      for( int p = 0 ; p < 12 ; p ++ ) {
+		double step_floor_r = floor( ( (double) p ) / 2.0 );
+		double step_floor_i = floor( ( (double) ( p - 1 ) ) / 2.0 );
+		for( int q = 0 ; q < step_floor_r ; q++ ) {
+		  temp[p] += exp_power_series_elements[2*q] * pow((double)-1,q) * complex_record_next->power_series[p - ( 2 * q )];
+		}
+		for( int q = 0 ; q <= step_floor_i ; q++ ) {
+		  temp[p] += im * exp_power_series_elements[2 * q + 1] * pow((double)-1,(q+1)) * complex_record_next->power_series[p - ( 2 * q ) - 1];
+		}
+	      }
+	      for ( int array_iter = 0 ; array_iter < 12 ; array_iter++ ) {
+		complex_record_current->power_series[array_iter].real() = temp[array_iter].real();
+		complex_record_current->power_series[array_iter].imag() = temp[array_iter].imag();
+	      }
+	    }
+	    complex_record_current->stored_data = true;
+	    complex_record_ref = complex_record_next;
+	    complex_record_current->next = complex_record_ref->next;
+	    delete complex_record_ref;
+	  }
+	}
+      }
+    } catch (std::exception& e) { //This section was part of my debugging, and may be removed.
+      std::cout << "Exception thrown: " << e.what() << std::endl;
+      ComplexRowVector exception_result (1);
+      exception_result(0) = std::complex<double> ( 0,0);
+      return exception_result;
+    }
+  }
+  return results;
+}
--- a/extra/lssa/lombnormcoeff.m	Tue Jun 19 11:41:05 2012 +0000
+++ b/extra/lssa/lombnormcoeff.m	Tue Jun 19 20:23:47 2012 +0000
@@ -1,9 +1,28 @@
 ## Copyright (c) 2012 Benjamin Lewis <benjf5@gmail.com>
-## GNU GPLv2
+## 
+## This program is free software; you can redistribute it and/or modify it under
+## the terms of the GNU General Public License as published by the Free Software
+## Foundation; either version 2 of the License, or (at your option) any later
+## version.
+##
+## This program is distributed in the hope that it will be useful, but WITHOUT
+## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+## details.
+##
+## You should have received a copy of the GNU General Public License along with
+## this program; if not, see <http://www.gnu.org/licenses/>.
 
-function coeff = lombnormcoeff(X,Y,omega)
-tau = atan2( sum( sin( 2.*omega.*X)), sum(cos(2.*omega.*X))) / 2;
-coeff = ( ( sum ( Y .* cos( omega .* X - tau ) ) .^ 2 ./ sum ( cos ( omega .* X - tau ) .^ 2 )
-	   + sum ( Y .* sin ( omega .* X - tau ) ) .^ 2 / sum ( sin ( omega .* X - tau ) .^ 2 ) )
-	 / ( 2 * var(Y) ) );
+## -*-texinfo-*-
+## @deftypefn {Function File} {c =} lombnormcoeff (time, mag, freq)
+##
+## Return the coefficient of the Lomb Normalised Periodogram at the
+## specified @var{frequency} of the periodogram applied to the
+## (@var{time}, @var{mag}) series.
+
+function coeff = lombnormcoeff(T,X,omega)
+tau = atan2( sum( sin( 2.*omega.*T)), sum(cos(2.*omega.*T))) / 2;
+coeff = ( ( sum ( X .* cos( omega .* T - tau ) ) .^ 2 ./ sum ( cos ( omega .* T - tau ) .^ 2 )
+	   + sum ( X .* sin ( omega .* T - tau ) ) .^ 2 / sum ( sin ( omega .* T - tau ) .^ 2 ) )
+	 / ( 2 * var(X) ) );
 end
\ No newline at end of file