-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate_pspectrum.py
411 lines (323 loc) · 15.8 KB
/
create_pspectrum.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
import math
import numpy as np
import time as tm
from detect_peaks import detect_peaks
import matplotlib.pyplot as plt
def numpy(y, t, freq_centre, half_width, resolution, chunk_size=50, dtype=np.double):
"""
Calculate powerspectrum using matrix multiplication with numpy
"""
pi = math.pi
t1 = tm.time()
# # Prepare input for use
# Convert input to numpy array (if not already)
y = np.asarray(y)
t = np.asarray(t)
# Change to angular frequencies (assumes input is in cyclic frequencies)
freq_centre[:] = [x * (2 * pi) for x in freq_centre]
half_width = half_width * 2 * pi
resolution = resolution * 2 * pi
# Data mean subtraction, to reduce potentially constant elements
y_mean = np.mean(y, dtype=dtype)
y = y - y_mean
t = t - np.min(t) # To get first element in t to be 0
# # Prepare for for-loop
# Preload results list
results = [None] * len(freq_centre)
# Set amount of steps (might be subject to rounding error)
step_amnt = int((2 * half_width) / resolution)
# # # # Run for loop for all frequencies indicated by length of freq_centre # # # #
for k in range(0, len(freq_centre)):
# Show numpy config to check which BLAS is used
# np.__config__.show()
# Get current frequency centre
freq_centre_current = freq_centre[k]
# Create frequency steps
freq = np.linspace(freq_centre_current - half_width, freq_centre_current + half_width, step_amnt, dtype=dtype)
# Reshape freq and t in order to do matrix multiplication
freq = np.reshape(freq, (len(freq), 1))
lent = len(t)
# # Zhu Li, do the thing
# Calculate sine and cosine function part values
sin = np.zeros(step_amnt)
cos = np.zeros(step_amnt)
sin2 = np.zeros(step_amnt)
cos2 = np.zeros(step_amnt)
sincos = np.zeros(step_amnt)
freq = np.ascontiguousarray(freq)
t = np.ascontiguousarray(t)
# Recurrence sine and cosine difference product
s_diff = np.sin(resolution * t, dtype=dtype)
c_diff = np.cos(resolution * t, dtype=dtype)
# # # # # Calculation matrices # # # # # # # # # # # # # # # # # # # # # # # # # # #
# use [c0, s0][c_diff, s_diff; -s_diff, c_diff] (inverted in calc for .T) #
# Recurrence based on s_m = c_(m-1)*sin(deltaf * t) + s_(m-1)*cos(deltaf * t) and #
# c_m = c_(m-1)*cos(deltaf * t) - s_(m-1)*sin(deltaf * t) from T. Ponman 1981 #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
calc_base = np.array([[c_diff, -s_diff], [s_diff, c_diff]]).T
calc_mat = np.zeros((chunk_size, lent, 2, 2))
calc_mat[0, :, :, :] = calc_base
# Generates the necessary matrices (multiplied) for each frequency point based on the original point
for i in range(1, chunk_size):
calc_mat[i, :, :, :] = np.matmul(calc_mat[i - 1, :, :, :], calc_base)
# Convert large matrix arrays to contigous arrays
calc_mat = np.ascontiguousarray(calc_mat)
# Calculates sine and cosine values
for i in range(0, step_amnt, chunk_size):
end = i + chunk_size
if end > step_amnt:
dif = end - step_amnt
calc_mat = np.delete(calc_mat, range(chunk_size-dif, chunk_size), 0)
end = step_amnt
chunk_size = end - i
print('Current chunk ', i, ':', end, ' of ', step_amnt)
# Original point calculation
s0 = np.sin(freq[i] * t, dtype=dtype)
c0 = np.cos(freq[i] * t, dtype=dtype)
# Sine/cosine vector initialization (for matmul calculation, see c0, s0 before loop)
trig_vec = np.zeros((1, lent, 1, 2))
trig_vec[0, :, 0, 0] = c0
trig_vec[0, :, 0, 1] = s0
trig_vec = np.repeat(trig_vec, chunk_size, axis=0)
# Matrix calculations)
matrix_result = np.matmul(trig_vec, calc_mat)
sin_temp = matrix_result[:, :, 0, 1]
cos_temp = matrix_result[:, :, 0, 0]
# Sum and save results
sin[i:end] = np.sum(y * sin_temp, 1)
cos[i:end] = np.sum(y * cos_temp, 1)
sin2[i:end] = np.sum(sin_temp ** 2, 1)
cos2[i:end] = np.sum(cos_temp ** 2, 1)
sincos[i:end] = np.sum(sin_temp * cos_temp, 1)
# # Calculate alpha and beta components of spectrum, and from them, power of spectrum
alpha = (sin * cos2 - cos * sincos) / (sin2 * cos2 - sincos**2)
beta = (cos * sin2 - sin * sincos) / (sin2 * cos2 - sincos**2)
power = alpha**2 + beta**2
# # Last loop steps
# Convert frequency back to cyclic units
freq = freq / (2 * pi)
# Save data in results
results[k] = [freq, power, alpha, beta]
t2 = tm.time()
print('Total time elapsed: ', t2-t1, ' seconds')
return results
def cuda(y, t, freq_centre, half_width, resolution, chunk_size=100, dtype=None, silent=False):
"""
Calculate powerspectrum using matrix multiplication with cupy (CUDA numpy)
"""
# # Uses cupy (cuda numpy) instead of numpy # #
import cupy as cu
if dtype is None:
dtype = cu.double
pi = math.pi
t1 = tm.time()
# # # # Prepare input for use # # # #
# Convert input to cupy array (if not already)
y = cu.asarray(y)
t = cu.asarray(t)
# Change to angular frequencies (assumes input is in cyclic frequencies)
freq_centre[:] = [x * (2 * pi) for x in freq_centre]
half_width = half_width * 2 * pi
resolution = resolution * 2 * pi
# Data mean subtraction, to reduce potentially constant elements
y_mean = cu.mean(y, dtype=dtype)
y = y - y_mean
t = t - cu.min(t) # To get first element in t to be 0
# # # # Prepare for for-loop # # # #
# Preload results list
results = [None] * len(freq_centre)
# Set amount of steps (might be subject to rounding error)
step_amnt = int((2 * half_width) / resolution)
# # # # Run for loop for all frequencies indicated by the list freq_centre # # # #
for k in range(0, len(freq_centre)):
freq_centre_current = freq_centre[k]
# Create frequency steps
freq = cu.linspace(freq_centre_current - half_width, freq_centre_current + half_width, step_amnt, dtype=dtype)
# Reshape freq in order to do matrix multiplication
freq = cu.reshape(freq, (len(freq), 1))
lent = len(t)
# # Prepare to calculate sine and cosine function parts of power spectrum estimate # #
sin = np.zeros(step_amnt)
cos = np.zeros(step_amnt)
sin2 = np.zeros(step_amnt)
cos2 = np.zeros(step_amnt)
sincos = np.zeros(step_amnt)
freq = cu.ascontiguousarray(freq)
t = cu.ascontiguousarray(t)
# Recurrence sine and cosine difference product
s_diff = cu.sin(resolution * t, dtype=dtype)
c_diff = cu.cos(resolution * t, dtype=dtype)
# # # # # Calculation matrices # # # # # # # # # # # # # # # # # # # # # # # # # # #
# use [c0, s0][c_diff, s_diff; -s_diff, c_diff] (inverted in calc for .T) #
# Recurrence based on s_m = c_(m-1)*sin(deltaf * t) + s_(m-1)*cos(deltaf * t) and #
# c_m = c_(m-1)*cos(deltaf * t) - s_(m-1)*sin(deltaf * t) from T. Ponman 1981 #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # Prepare linear operation matrix with initial operation # #
calc_base = cu.array([[c_diff, -s_diff], [s_diff, c_diff]]).T
calc_mat = cu.zeros((chunk_size, lent, 2, 2))
calc_mat[0, :, :, :] = calc_base
# # Generate linear operation matrices for recurrence multiplication # #
for i in range(1, chunk_size):
calc_mat[i, :, :, :] = cu.matmul(calc_mat[i - 1, :, :, :], calc_base)
# Convert large matrix arrays to contiguous arrays
calc_mat = cu.ascontiguousarray(calc_mat)
# # Calculate sine and cosine function parts of power spectrum estimation # #
for i in range(0, step_amnt, chunk_size):
end = i + chunk_size
if end > step_amnt:
dif = end - step_amnt
calc_mat = cu.asnumpy(calc_mat)
calc_mat = np.delete(calc_mat, range(chunk_size - dif, chunk_size), 0)
calc_mat = cu.array(calc_mat)
calc_mat = cu.ascontiguousarray(calc_mat)
end = step_amnt
chunk_size = end - i
if not silent:
print('Current chunk ', i, ':', end, ' of ', step_amnt)
# Original point calculation
s0 = cu.sin(freq[i] * t, dtype=dtype)
c0 = cu.cos(freq[i] * t, dtype=dtype)
# Sine/cosine vector initialization (for matmul calculation, see c0, s0 before loop)
trig_vec = cu.zeros((1, lent, 1, 2))
trig_vec[0, :, 0, 0] = c0
trig_vec[0, :, 0, 1] = s0
trig_vec = cu.repeat(trig_vec, chunk_size, axis=0)
# Matrix calculations
matrix_result = cu.matmul(trig_vec, calc_mat)
sin_temp = matrix_result[:, :, 0, 1]
cos_temp = matrix_result[:, :, 0, 0]
# # Sum and save results # #
sin[i:end] = cu.sum(y * sin_temp, 1).get()
cos[i:end] = cu.sum(y * cos_temp, 1).get()
sin2[i:end] = cu.sum(sin_temp ** 2, 1).get()
cos2[i:end] = cu.sum(cos_temp ** 2, 1).get()
sincos[i:end] = cu.sum(sin_temp * cos_temp, 1).get()
# # # # Calculate alpha and beta components of spectrum, and from them, power of spectrum # # # #
alpha = (sin * cos2 - cos * sincos) / (sin2 * cos2 - sincos**2)
beta = (cos * sin2 - sin * sincos) / (sin2 * cos2 - sincos**2)
power = alpha**2 + beta**2
# # # # Last loop steps # # # #
# Convert frequency back to cyclic units
freq = freq.get() / (2 * pi)
# Save data in results
results[k] = [freq, power, alpha, beta]
t2 = tm.time()
# Change freq_centre back to cyclic frequencies
freq_centre[:] = [x / (2 * pi) for x in freq_centre]
if not silent:
print('Total time elapsed create_pspectrum_cuda: ', t2-t1, ' seconds')
return results
def numpar(y, t, freq_centre, half_width, resolution, chunk_size=50, dtype=np.double, n_jobs=2):
from joblib import Parallel, delayed
pi = math.pi
t1 = tm.time()
# # Prepare input for use
# Convert input to numpy array (if not already)
y = np.asarray(y)
t = np.asarray(t)
# Change to angular frequencies (assumes input is in cyclic frequencies)
freq_centre[:] = [x * (2 * pi) for x in freq_centre]
half_width = half_width * 2 * pi
resolution = resolution * 2 * pi
# Data mean subtraction, to reduce potentially constant elements
y_mean = np.mean(y, dtype=dtype)
y = y - y_mean
t = t - np.min(t) # To get first element in t to be 0
# # Prepare for for-loop
# Preload results list
results = [None] * len(freq_centre)
# Set amount of steps (might be subject to rounding error)
step_amnt = int((2 * half_width) / resolution)
# # # # Run for loop for all frequencies indicated by length of freq_centre # # # #
for k in range(0, len(freq_centre)):
# Show numpy config to check which BLAS is used
# np.__config__.show()
# Get current frequency centre
freq_centre_current = freq_centre[k]
# Create frequency steps
freq = np.linspace(freq_centre_current - half_width, freq_centre_current + half_width, step_amnt, dtype=dtype)
# Reshape freq and t in order to do matrix multiplication
freq = np.reshape(freq, (len(freq), 1))
lent = len(t)
# # Zhu Li, do the thing
# Calculate sine and cosine function part values
sin = np.zeros(step_amnt)
cos = np.zeros(step_amnt)
sin2 = np.zeros(step_amnt)
cos2 = np.zeros(step_amnt)
sincos = np.zeros(step_amnt)
freq = np.ascontiguousarray(freq)
t = np.ascontiguousarray(t)
# Recurrence sine and cosine difference product
s_diff = np.sin(resolution * t, dtype=dtype)
c_diff = np.cos(resolution * t, dtype=dtype)
# # # # # Calculation matrices # # # # # # # # # # # # # # # # # # # # # # # # # # #
# use [c0, s0][c_diff, s_diff; -s_diff, c_diff] (inverted in calc for .T) #
# Recurrence based on s_m = c_(m-1)*sin(deltaf * t) + s_(m-1)*cos(deltaf * t) and #
# c_m = c_(m-1)*cos(deltaf * t) - s_(m-1)*sin(deltaf * t) from T. Ponman 1981 #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
calc_base = np.array([[c_diff, -s_diff], [s_diff, c_diff]]).T
calc_mat = np.zeros((chunk_size, lent, 2, 2))
calc_mat[0, :, :, :] = calc_base
# Generates the necessary matrices (multiplied) for each frequency point based on the original point
for i in range(1, chunk_size):
calc_mat[i, :, :, :] = np.matmul(calc_mat[i - 1, :, :, :], calc_base)
# Convert large matrix arrays to contigous arrays
calc_mat = np.ascontiguousarray(calc_mat)
# Calculates sine and cosine values
parres = Parallel(n_jobs=n_jobs)(delayed(__process_input)(i, chunk_size, calc_mat, step_amnt, dtype, freq, lent,
t, y)
for i in range(0, step_amnt, chunk_size))
for x in parres:
[i, end, sin_temp, cos_temp, sin2_temp, cos2_temp, sincos_temp] = x
sin[i:end] = sin_temp
sin2[i:end] = sin2_temp
cos[i:end] = cos_temp
cos2[i:end] = cos2_temp
sincos[i:end] = sincos_temp
# # Calculate alpha and beta components of spectrum, and from them, power of spectrum
alpha = (sin * cos2 - cos * sincos) / (sin2 * cos2 - sincos ** 2)
beta = (cos * sin2 - sin * sincos) / (sin2 * cos2 - sincos ** 2)
power = alpha ** 2 + beta ** 2
# # Last loop steps
# Convert frequency back to cyclic units
freq = freq / (2 * pi)
# Save data in results
results[k] = [freq, power, alpha, beta]
t2 = tm.time()
print('Total time elapsed: ', t2 - t1, ' seconds')
return results
def __process_input(i, chunk_size, calc_mat, step_amnt, dtype, freq, lent, t, y):
end = i + chunk_size
if end > step_amnt:
dif = end - step_amnt
calc_mat = np.delete(calc_mat, range(chunk_size - dif, chunk_size), 0)
end = step_amnt
chunk_size = end - i
print('Current chunk ', i, ':', end, ' of ', step_amnt)
# Original point calculation
s0 = np.sin(freq[i] * t, dtype=dtype)
c0 = np.cos(freq[i] * t, dtype=dtype)
# Sine/cosine vector initialization (for matmul calculation, see c0, s0 before loop)
trig_vec = np.zeros((1, lent, 1, 2))
trig_vec[0, :, 0, 0] = c0
trig_vec[0, :, 0, 1] = s0
trig_vec = np.repeat(trig_vec, chunk_size, axis=0)
# Matrix calculations)
matrix_result = np.matmul(trig_vec, calc_mat)
sin_temp = matrix_result[:, :, 0, 1]
cos_temp = matrix_result[:, :, 0, 0]
sin = np.sum(y * sin_temp, 1)
cos = np.sum(y * cos_temp, 1)
sin2 = np.sum(sin_temp ** 2, 1)
cos2 = np.sum(cos_temp ** 2, 1)
sincos = np.sum(sin_temp * cos_temp, 1)
return [i, end, sin, cos, sin2, cos2, sincos]
def nufftpy(y, t, half_width, resolution):
import nufftpy as nfpy
steps = int((2*half_width)/resolution)
freq = nfpy.nufftfreqs(steps, df=resolution)
freq = freq[len(freq)//2:-1]
harmonic_content = nfpy.nufft1(t, y, steps, df=(resolution*2*math.pi))
harmonic_content = harmonic_content[len(harmonic_content)//2:-1]
return [freq, harmonic_content.real**2+harmonic_content.imag**2, harmonic_content.real, harmonic_content.imag]