1 '''
  2 Transform of length 2N real sequence using length N complex FFT. 
  3 Python 2.7.3
  4 John Bryan, 2017
  5 '''
  6 
  7 import numpy as np
  8 import time
  9 import matplotlib.pyplot as plt
 10 import warnings
 11 np.set_printoptions(threshold=np.nan, precision=3, suppress=1)
 12 warnings.filterwarnings("ignore")
 13 
 14 
 15 def bitreversal(xarray, length, log2length):
 16     ''' 
 17     bracewell-buneman bit reversal function
 18     inputs: xarray is array; length is array length; log2length=log2(length).
 19     output: bit reversed array xarray. 
 20     '''
 21     muplus = int((log2length+1)/2)
 22     mvar = 1
 23     reverse = np.zeros(length, dtype = int)
 24     upper_range = muplus+1
 25     for nuvar in np.arange(1, upper_range):
 26         for kvar in np.arange(0, mvar):
 27             tvar = 2*reverse[kvar]
 28             reverse[kvar] = tvar
 29             reverse[kvar+mvar] = tvar+1
 30         mvar = mvar+mvar
 31     if (int(log2length) & 0x01):
 32         mvar = mvar/2
 33     for qvar in np.arange(1, mvar):
 34         nprime = qvar-mvar
 35         rprimeprime = reverse[qvar]*mvar
 36         for pvar in np.arange(0, reverse[qvar]):
 37             nprime = nprime+mvar
 38             rprime = rprimeprime+reverse[pvar]
 39             temp = xarray[nprime]
 40             xarray[nprime] = xarray[rprime]
 41             xarray[rprime] = temp
 42     return xarray
 43 
 44 
 45 def fft0 (xarray, twiddle, log2length) :
 46     '''
 47     radix-2 dit
 48     '''
 49     nvar = xarray.size
 50     b_p = nvar/2
 51     nvar_p = 2
 52     twiddle_step_size = nvar/2
 53     for pvar in range(0,  log2length):
 54         nvar_pp =  nvar_p/2
 55         base_t = 0
 56         for bvar in range(0,  b_p):
 57             base_b = base_t+nvar_pp
 58             for nvar in range(0,  nvar_pp):
 59                 if nvar == 0:
 60                     bot = xarray[base_b+nvar]
 61                 else:
 62                     twiddle_factor = nvar*twiddle_step_size
 63                     bot = xarray[base_b+nvar]*twiddle[twiddle_factor]
 64                 top = xarray[base_t+nvar]
 65                 xarray[base_t+nvar] = top+bot
 66                 xarray[base_b+nvar] = top-bot
 67             base_t = base_t+nvar_p
 68         b_p = b_p/2
 69         nvar_p = nvar_p*2
 70         twiddle_step_size = twiddle_step_size/2
 71     return xarray
 72 
 73 
 74 def steps(garray, halftwiddles, twiddles, length, log2length):
 75     ''' 
 76     steps function carries out the steps to calculate transform.    
 77     Input garray is real sequence.
 78     Output g2array is transform of garray. 
 79     '''
 80     n2var = length/2
 81     g2array = np.zeros(length, dtype=np.complex_)
 82     x1array = np.zeros(n2var, dtype=np.complex_)
 83     x2array = np.zeros(n2var, dtype=np.complex_)
 84     y1array = np.zeros(n2var, dtype=np.complex_ )
 85     y2array = np.zeros(n2var, dtype=np.complex_ )
 86     # x1array is even-indexed values of garray.  
 87     x1array = garray[::2]                         # (eq.6) 
 88     # x2array is odd-indexed values of garray. 
 89     x2array = garray[1::2]                        # (eq.7) 
 90     x2j = [1j*mvar for mvar in x2array]
 91     xarray = [avar+bvar for avar, bvar in zip(x1array, x2j)]   # (eq.8)
 92     x0array = np.array(xarray)
 93     x_array = x0array.astype(np.complex_)
 94     halflength = int(x_array.size)
 95     log2halflength = int(np.log2(halflength))
 96     z0array = bitreversal(x_array, halflength, log2halflength)
 97     x3array = fft0(z0array, halftwiddles, log2halflength)  # (eq.9)
 98     x3conj = np.conjugate(x3array)
 99     for kvar in range(1, n2var):                           # (eqs.10,11)
100        y1array[kvar] = 0.5*(x3array[kvar]+x3conj[n2var-kvar])
101        y2array[kvar] = -0.5*1j*(x3array[kvar]-x3conj[n2var-kvar])
102    y1array[0] = 0.5*(x3array[0]+x3conj[0])
103    y2array[0] = -0.5*1j*(x3array[0]-x3conj[0])
104    for kvar in range(0, n2var):
105        g2array[kvar] = y1array[kvar]+(twiddles[kvar]*y2array[kvar])   # (eq.12)
106    g2array[n2var] = 0.5*((x3array[0]+x3conj[0])+1j*(x3array[0]-x3conj[0]))     # (eq.13)
107    for kvar in range (1, n2var):
108        g2array[length-kvar] = np.conjugate(g2array[kvar])          # (eq.14) 
109    return g2array
110 
111 
112 
113 def plot_times(times2, times3):
114    '''
115    plot performance as a function of sequence length
116    '''
117    lengths = np.zeros(15, dtype=int)
118    for i in range(4, 19):
119        lengths[i-4] = np.power(2, i)
120    plt.figure(figsize=(7, 5))
121    plt.rc("font", size=9)
122    plt.loglog(lengths, times2, 'o', ms=5, markerfacecolor="None", markeredgecolor='blue', \
123    markeredgewidth=1, basex=2, basey=10, label='Half-length FFT plus additional steps')
124    plt.loglog(lengths, times3, '^', ms=5, markerfacecolor="None", markeredgecolor='green', \
125    markeredgewidth=1, basex=2, basey=10, label='Full-length complex FFT')
126    plt.legend(loc=2)
127    plt.grid()
128    plt.xlim([9, 530000])
129    plt.ylim([.00001, 8])
130    plt.ylabel("time (seconds)")
131    plt.xlabel("sequence length")
132    plt.title("Performance vs Sequence Length")
133    plt.savefig('t22.png', bbox_inches='tight')
134    plt.show()
135    return None
136 
137 
138 def test():
139    '''
140    Test w/ different length random sequences  
141    '''
142    flag = 0
143    i = 0
144    times = np.zeros(15)
145    times2 = np.zeros(15)
146    for log2length in range (4, 19):
147        length = np.power(2, log2length)
148        # Generate random real seq. g w/ length=power of 2.  
149        np.random.seed(0)
150        garray = np.random.rand(length)
151        np.random.seed(0)
152        garray0 = np.random.rand(length)
153        np.random.seed(0)
154        garray00 = np.random.rand(length)
155        # Input garray00 to numpy fft to get gpy to compare with. 
156        gpy = np.fft.fft(garray00)
157        n2var = garray.size/2
158        twiddles = np.zeros(n2var, dtype=np.complex)
159        halftwiddles = np.exp(-2j*np.pi*np.arange(0, 0.5, 1./(0.5*garray.size), dtype=np.complex_))
160        twiddles = np.exp(-2j*np.pi*np.arange(0, 0.5, 1./(garray.size), dtype=np.complex_))
161        t0time = time.time()
162        g2array = steps(garray, halftwiddles, twiddles, length, log2length)
163        times[i] = time.time()-t0time
164        t0time = time.time()
165        garray0 = bitreversal(garray0, length, log2length)
166        garray0 = garray0.astype(np.complex_)
167        g3array = fft0(garray0, twiddles, log2length)
168        times2[i] = time.time()-t0time
169        t_f = np.allclose(g3array, g2array)
170        if t_f == 0:
171            flag = 1
172        # assert if false 
173        assert(t_f)
174        print t_f
175        i = i+1
176    if flag == 0:
177        print ("All results were correct.")
178    print 'Algorithm A (half-length complex fft with additional steps) fft time is', times
179    print 'Algorithm B (full-length complex fft) time is', times2
180    plot_times(times, times2)
181 
182 
183 test()