pyNFFT3 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyNFFT3/NFCT.py +340 -0
- pyNFFT3/NFFT.py +326 -0
- pyNFFT3/NFMT.py +1 -0
- pyNFFT3/NFST.py +335 -0
- pyNFFT3/__init__.py +165 -0
- pyNFFT3/fastsum.py +325 -0
- pyNFFT3/flags.py +231 -0
- pyNFFT3/lib/AVX/libfastsumjulia.dll +0 -0
- pyNFFT3/lib/AVX/libfastsumjulia.dylib +0 -0
- pyNFFT3/lib/AVX/libfastsumjulia.so +0 -0
- pyNFFT3/lib/AVX/libnfctjulia.dll +0 -0
- pyNFFT3/lib/AVX/libnfctjulia.dylib +0 -0
- pyNFFT3/lib/AVX/libnfctjulia.so +0 -0
- pyNFFT3/lib/AVX/libnfftjulia.dll +0 -0
- pyNFFT3/lib/AVX/libnfftjulia.dylib +0 -0
- pyNFFT3/lib/AVX/libnfftjulia.so +0 -0
- pyNFFT3/lib/AVX/libnfstjulia.dll +0 -0
- pyNFFT3/lib/AVX/libnfstjulia.dylib +0 -0
- pyNFFT3/lib/AVX/libnfstjulia.so +0 -0
- pyNFFT3/lib/AVX2/libfastsumjulia.dll +0 -0
- pyNFFT3/lib/AVX2/libfastsumjulia.dylib +0 -0
- pyNFFT3/lib/AVX2/libfastsumjulia.so +0 -0
- pyNFFT3/lib/AVX2/libnfctjulia.dll +0 -0
- pyNFFT3/lib/AVX2/libnfctjulia.dylib +0 -0
- pyNFFT3/lib/AVX2/libnfctjulia.so +0 -0
- pyNFFT3/lib/AVX2/libnfftjulia.dll +0 -0
- pyNFFT3/lib/AVX2/libnfftjulia.dylib +0 -0
- pyNFFT3/lib/AVX2/libnfftjulia.so +0 -0
- pyNFFT3/lib/AVX2/libnfstjulia.dll +0 -0
- pyNFFT3/lib/AVX2/libnfstjulia.dylib +0 -0
- pyNFFT3/lib/AVX2/libnfstjulia.so +0 -0
- pyNFFT3/lib/SSE2/libfastsumjulia.dll +0 -0
- pyNFFT3/lib/SSE2/libfastsumjulia.dylib +0 -0
- pyNFFT3/lib/SSE2/libfastsumjulia.so +0 -0
- pyNFFT3/lib/SSE2/libnfctjulia.dll +0 -0
- pyNFFT3/lib/SSE2/libnfctjulia.dylib +0 -0
- pyNFFT3/lib/SSE2/libnfctjulia.so +0 -0
- pyNFFT3/lib/SSE2/libnfftjulia.dll +0 -0
- pyNFFT3/lib/SSE2/libnfftjulia.dylib +0 -0
- pyNFFT3/lib/SSE2/libnfftjulia.so +0 -0
- pyNFFT3/lib/SSE2/libnfstjulia.dll +0 -0
- pyNFFT3/lib/SSE2/libnfstjulia.dylib +0 -0
- pyNFFT3/lib/SSE2/libnfstjulia.so +0 -0
- pynfft3-0.2.1.dist-info/METADATA +40 -0
- pynfft3-0.2.1.dist-info/RECORD +47 -0
- pynfft3-0.2.1.dist-info/WHEEL +4 -0
- pynfft3-0.2.1.dist-info/licenses/LICENSE +674 -0
pyNFFT3/NFCT.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import numpy as np
|
|
3
|
+
from .flags import *
|
|
4
|
+
from . import _nfctlib
|
|
5
|
+
from . import nfct_plan
|
|
6
|
+
|
|
7
|
+
# Set arugment and return types for functions
|
|
8
|
+
_nfctlib.jnfct_init.argtypes = [
|
|
9
|
+
ctypes.POINTER(nfct_plan),
|
|
10
|
+
ctypes.c_int32,
|
|
11
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
12
|
+
ctypes.c_int32,
|
|
13
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
14
|
+
ctypes.c_int32,
|
|
15
|
+
ctypes.c_uint32,
|
|
16
|
+
ctypes.c_uint32,
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
_nfctlib.jnfct_alloc.restype = ctypes.POINTER(nfct_plan)
|
|
20
|
+
_nfctlib.jnfct_finalize.argtypes = [ctypes.POINTER(nfct_plan)]
|
|
21
|
+
|
|
22
|
+
_nfctlib.jnfct_set_x.argtypes = [
|
|
23
|
+
ctypes.POINTER(nfct_plan),
|
|
24
|
+
np.ctypeslib.ndpointer(np.float64, flags="C"),
|
|
25
|
+
]
|
|
26
|
+
_nfctlib.jnfct_set_f.argtypes = [
|
|
27
|
+
ctypes.POINTER(nfct_plan),
|
|
28
|
+
np.ctypeslib.ndpointer(np.float64, ndim=1, flags="C"),
|
|
29
|
+
]
|
|
30
|
+
_nfctlib.jnfct_set_fhat.argtypes = [
|
|
31
|
+
ctypes.POINTER(nfct_plan),
|
|
32
|
+
np.ctypeslib.ndpointer(np.float64, ndim=1, flags="C"),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
_nfctlib.jnfct_trafo.argtypes = [ctypes.POINTER(nfct_plan)]
|
|
36
|
+
_nfctlib.jnfct_adjoint.argtypes = [ctypes.POINTER(nfct_plan)]
|
|
37
|
+
_nfctlib.jnfct_trafo_direct.argtypes = [ctypes.POINTER(nfct_plan)]
|
|
38
|
+
_nfctlib.jnfct_adjoint_direct.argtypes = [ctypes.POINTER(nfct_plan)]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class NFCT:
|
|
42
|
+
def __init__(self, N, M, n=None, m=default_window_cut_off, f1=None, f2=f2_default):
|
|
43
|
+
"""
|
|
44
|
+
Class to perform non-equispaced fast cosine transforms (NFCT)
|
|
45
|
+
with a dimension of **D**.
|
|
46
|
+
Just **N** and **M** are required for initializing a plan.
|
|
47
|
+
"""
|
|
48
|
+
self.plan = _nfctlib.jnfct_alloc()
|
|
49
|
+
self.N = N # bandwidth tuple
|
|
50
|
+
N_ct = N.ctypes.data_as(ctypes.POINTER(ctypes.c_int32))
|
|
51
|
+
self.M = M # number of nodes
|
|
52
|
+
self.n = n # oversampling per dimension
|
|
53
|
+
self.m = m # window size
|
|
54
|
+
self.D = len(N) # dimensions
|
|
55
|
+
|
|
56
|
+
if any(x <= 0 for x in N):
|
|
57
|
+
raise ValueError(f"Invalid N: {N}. Argument must be a positive integer")
|
|
58
|
+
|
|
59
|
+
if sum(x % 2 for x in N) != 0:
|
|
60
|
+
raise ValueError(f"Invalid N: {N}. Argument must be an even integer")
|
|
61
|
+
|
|
62
|
+
if M <= 0:
|
|
63
|
+
raise ValueError(f"Invalid M: {M}. Argument must be a positive integer")
|
|
64
|
+
|
|
65
|
+
if n is None:
|
|
66
|
+
self.n = (2 ** (np.ceil(np.log(self.N) / np.log(2)) + 1)).astype("int32")
|
|
67
|
+
n_ct = self.n.ctypes.data_as(ctypes.POINTER(ctypes.c_int32))
|
|
68
|
+
|
|
69
|
+
if any(x <= 0 for x in self.n):
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"Invalid n: {self.n}. Argument must be a positive integer"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if any(x <= y for x, y in zip(self.n, N)):
|
|
75
|
+
raise ValueError(f"Invalid n: {self.n}. Argument must fulfil n_i > N_i")
|
|
76
|
+
|
|
77
|
+
if sum(x % 2 for x in self.n) != 0:
|
|
78
|
+
raise ValueError(f"Invalid n: {self.n}. Argument must be an even integer")
|
|
79
|
+
|
|
80
|
+
if m <= 0:
|
|
81
|
+
raise ValueError(f"Invalid m: {m}. Argument must be a positive integer")
|
|
82
|
+
|
|
83
|
+
if f1 is None:
|
|
84
|
+
self.f1 = f1_default if self.D > 1 else f1_default_1d
|
|
85
|
+
else:
|
|
86
|
+
self.f1 = f1
|
|
87
|
+
|
|
88
|
+
self.f2 = f2 # FFTW flags
|
|
89
|
+
_nfctlib.jnfct_init(
|
|
90
|
+
self.plan, self.D, N_ct, self.M, n_ct, self.m, self.f1, self.f2
|
|
91
|
+
)
|
|
92
|
+
self.init_done = True # bool for plan init
|
|
93
|
+
self.finalized = False # bool for finalizer
|
|
94
|
+
self.x = None # nodes, will be set later
|
|
95
|
+
self.f = None # function values
|
|
96
|
+
self.fhat = None # Fourier coefficients
|
|
97
|
+
|
|
98
|
+
def __del__(self):
|
|
99
|
+
self.finalize_plan()
|
|
100
|
+
|
|
101
|
+
def nfct_finalize_plan(self):
|
|
102
|
+
"""
|
|
103
|
+
Finalizes an NFCT plan.
|
|
104
|
+
This function does not have to be called by the user.
|
|
105
|
+
"""
|
|
106
|
+
_nfctlib.jnfct_finalize.argtypes = (ctypes.POINTER(nfct_plan),) # P
|
|
107
|
+
|
|
108
|
+
if not self.init_done:
|
|
109
|
+
raise ValueError("NFST not initialized.")
|
|
110
|
+
|
|
111
|
+
if not self.finalized:
|
|
112
|
+
self.finalized = True
|
|
113
|
+
_nfctlib.jnfct_finalize(self.plan)
|
|
114
|
+
|
|
115
|
+
def finalize_plan(self):
|
|
116
|
+
"""
|
|
117
|
+
Alternative call for **nfct_finalize_plan()**
|
|
118
|
+
"""
|
|
119
|
+
return self.nfct_finalize_plan()
|
|
120
|
+
|
|
121
|
+
def nfct_init(self):
|
|
122
|
+
"""
|
|
123
|
+
Initializes the NFCT plan in C.
|
|
124
|
+
This function does not have to be called by the user.
|
|
125
|
+
"""
|
|
126
|
+
# Convert N and n to numpy arrays for passing them to C
|
|
127
|
+
Nv = np.array(self.N, dtype=np.int32)
|
|
128
|
+
n = np.array(self.n, dtype=np.int32)
|
|
129
|
+
|
|
130
|
+
# Call init for memory allocation
|
|
131
|
+
ptr = _nfctlib.jnfct_alloc()
|
|
132
|
+
|
|
133
|
+
# Set the pointer
|
|
134
|
+
self.plan = ctypes.cast(ptr, ctypes.POINTER(nfct_plan))
|
|
135
|
+
|
|
136
|
+
# Initialize values
|
|
137
|
+
_nfctlib.jnfct_init(
|
|
138
|
+
self.plan,
|
|
139
|
+
ctypes.c_int(self.D),
|
|
140
|
+
ctypes.cast(Nv.ctypes.data, ctypes.POINTER(ctypes.c_int)),
|
|
141
|
+
ctypes.c_int(self.M),
|
|
142
|
+
ctypes.cast(n.ctypes.data, ctypes.POINTER(ctypes.c_int)),
|
|
143
|
+
ctypes.c_int(self.m),
|
|
144
|
+
ctypes.c_uint(self.f1),
|
|
145
|
+
ctypes.c_uint(self.f2),
|
|
146
|
+
)
|
|
147
|
+
self.init_done = True
|
|
148
|
+
|
|
149
|
+
def init(self):
|
|
150
|
+
"""
|
|
151
|
+
Alternative call for **nfft_init()**
|
|
152
|
+
"""
|
|
153
|
+
return self.nfct_init()
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def x(self):
|
|
157
|
+
return self._X
|
|
158
|
+
|
|
159
|
+
@x.setter
|
|
160
|
+
def x(self, value):
|
|
161
|
+
if value is not None:
|
|
162
|
+
if not (
|
|
163
|
+
isinstance(value, np.ndarray)
|
|
164
|
+
and value.dtype == np.float64
|
|
165
|
+
and value.flags["C"]
|
|
166
|
+
):
|
|
167
|
+
raise RuntimeError("x has to be C-continuous, numpy float64 array")
|
|
168
|
+
if self.D == 1:
|
|
169
|
+
_nfctlib.jnfct_set_x.restype = np.ctypeslib.ndpointer(
|
|
170
|
+
dtype=np.float64, ndim=2, shape=self.M, flags="C"
|
|
171
|
+
)
|
|
172
|
+
else:
|
|
173
|
+
_nfctlib.jnfct_set_x.restype = np.ctypeslib.ndpointer(
|
|
174
|
+
dtype=np.float64, ndim=2, shape=(self.M, self.D), flags="C"
|
|
175
|
+
)
|
|
176
|
+
self._X = _nfctlib.jnfct_set_x(self.plan, value)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def f(self):
|
|
180
|
+
return self._f
|
|
181
|
+
|
|
182
|
+
@f.setter
|
|
183
|
+
def f(self, value):
|
|
184
|
+
if value is not None:
|
|
185
|
+
if not (
|
|
186
|
+
isinstance(value, np.ndarray)
|
|
187
|
+
and value.dtype == np.float64
|
|
188
|
+
and value.flags["C"]
|
|
189
|
+
):
|
|
190
|
+
raise RuntimeError("f has to be C-continuous, numpy float64 array")
|
|
191
|
+
_nfctlib.jnfct_set_f.restype = np.ctypeslib.ndpointer(
|
|
192
|
+
np.float64, ndim=1, shape=self.M, flags="C"
|
|
193
|
+
)
|
|
194
|
+
self._f = _nfctlib.jnfct_set_f(self.plan, value)
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def fhat(self):
|
|
198
|
+
return self._fhat
|
|
199
|
+
|
|
200
|
+
@fhat.setter
|
|
201
|
+
def fhat(self, value):
|
|
202
|
+
Ns = np.prod(self.N)
|
|
203
|
+
if value is not None:
|
|
204
|
+
if not (isinstance(value, np.ndarray) and value.dtype == np.float64):
|
|
205
|
+
raise RuntimeError("fhat has to be a numpy float64 array")
|
|
206
|
+
if not value.flags["C"]:
|
|
207
|
+
raise RuntimeError("fhat has to be C-continuous")
|
|
208
|
+
if value.size != Ns:
|
|
209
|
+
raise ValueError(f"fhat has to be an array of size {Ns}")
|
|
210
|
+
_nfctlib.jnfct_set_fhat.argtypes = [
|
|
211
|
+
ctypes.POINTER(nfct_plan),
|
|
212
|
+
np.ctypeslib.ndpointer(np.float64, ndim=1, flags="C"),
|
|
213
|
+
]
|
|
214
|
+
_nfctlib.jnfct_set_fhat.restype = np.ctypeslib.ndpointer(
|
|
215
|
+
np.float64, ndim=1, shape=Ns, flags="C_CONTIGUOUS"
|
|
216
|
+
)
|
|
217
|
+
self._fhat = _nfctlib.jnfct_set_fhat(self.plan, value)
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def num_threads(self):
|
|
221
|
+
return _nfctlib.nfft_get_num_threads()
|
|
222
|
+
|
|
223
|
+
def nfct_trafo(self):
|
|
224
|
+
"""
|
|
225
|
+
Computes the NDCT via the fast NFCT algorithm for the provided nodes in **x** and coefficients in **fhat**.
|
|
226
|
+
"""
|
|
227
|
+
Ns = np.prod(self.N)
|
|
228
|
+
_nfctlib.jnfct_trafo.restype = np.ctypeslib.ndpointer(
|
|
229
|
+
np.float64, shape=Ns, flags="C"
|
|
230
|
+
)
|
|
231
|
+
# Prevent bad stuff from happening
|
|
232
|
+
if self.finalized:
|
|
233
|
+
raise RuntimeError("NFCT already finalized")
|
|
234
|
+
|
|
235
|
+
if not hasattr(self, "fhat"):
|
|
236
|
+
raise ValueError("fhat has not been set.")
|
|
237
|
+
|
|
238
|
+
if not hasattr(self, "x"):
|
|
239
|
+
raise ValueError("x has not been set.")
|
|
240
|
+
|
|
241
|
+
ptr = _nfctlib.jnfct_trafo(self.plan)
|
|
242
|
+
self.f = ptr
|
|
243
|
+
|
|
244
|
+
def trafo(self):
|
|
245
|
+
"""
|
|
246
|
+
Alternative call for **nfct_trafo()**
|
|
247
|
+
"""
|
|
248
|
+
return self.nfct_trafo()
|
|
249
|
+
|
|
250
|
+
def nfct_trafo_direct(self):
|
|
251
|
+
"""
|
|
252
|
+
Computes the NDCT via naive matrix-vector multiplication for the provided nodes in **x** and coefficients in **fhat**.
|
|
253
|
+
"""
|
|
254
|
+
_nfctlib.jnfct_trafo_direct.restype = np.ctypeslib.ndpointer(
|
|
255
|
+
np.float64, shape=self.M, flags="C"
|
|
256
|
+
)
|
|
257
|
+
# Prevent bad stuff from happening
|
|
258
|
+
if self.finalized:
|
|
259
|
+
raise RuntimeError("NFCT already finalized")
|
|
260
|
+
|
|
261
|
+
if self.fhat is None:
|
|
262
|
+
raise ValueError("fhat has not been set.")
|
|
263
|
+
|
|
264
|
+
if self.x is None:
|
|
265
|
+
raise ValueError("x has not been set.")
|
|
266
|
+
|
|
267
|
+
ptr = _nfctlib.jnfct_trafo_direct(self.plan)
|
|
268
|
+
self.f = ptr
|
|
269
|
+
|
|
270
|
+
def trafo_direct(self):
|
|
271
|
+
"""
|
|
272
|
+
Alternative call for **nfct_trafo_direct()**
|
|
273
|
+
"""
|
|
274
|
+
return self.nfct_trafo_direct()
|
|
275
|
+
|
|
276
|
+
def nfct_transposed(self):
|
|
277
|
+
"""
|
|
278
|
+
Computes the transposed NDCT via the fast transposed NFCT algorithm for the provided nodes in **x** and coefficients in **f**.
|
|
279
|
+
"""
|
|
280
|
+
Ns = np.prod(self.N)
|
|
281
|
+
_nfctlib.jnfct_adjoint.restype = np.ctypeslib.ndpointer(
|
|
282
|
+
np.float64, shape=Ns, flags="C"
|
|
283
|
+
)
|
|
284
|
+
# Prevent bad stuff from happening
|
|
285
|
+
if self.finalized:
|
|
286
|
+
raise RuntimeError("NFCT already finalized")
|
|
287
|
+
|
|
288
|
+
if self.f is None:
|
|
289
|
+
raise ValueError("f has not been set.")
|
|
290
|
+
|
|
291
|
+
if self.x is None:
|
|
292
|
+
raise ValueError("x has not been set.")
|
|
293
|
+
|
|
294
|
+
ptr = _nfctlib.jnfct_adjoint(self.plan)
|
|
295
|
+
self.fhat = ptr
|
|
296
|
+
|
|
297
|
+
def nfct_transposed_direct(self):
|
|
298
|
+
"""
|
|
299
|
+
Computes the transposed NDCT via naive matrix-vector multiplication for provided nodes for the provided nodes in **x** and coefficients in **f**.
|
|
300
|
+
"""
|
|
301
|
+
Ns = np.prod(self.N)
|
|
302
|
+
_nfctlib.jnfct_adjoint_direct.restype = np.ctypeslib.ndpointer(
|
|
303
|
+
np.float64, shape=Ns, flags="C"
|
|
304
|
+
)
|
|
305
|
+
# Prevent bad stuff from happening
|
|
306
|
+
if self.finalized:
|
|
307
|
+
raise RuntimeError("NFCT already finalized")
|
|
308
|
+
|
|
309
|
+
if self.f is None:
|
|
310
|
+
raise ValueError("f has not been set.")
|
|
311
|
+
|
|
312
|
+
if self.x is None:
|
|
313
|
+
raise ValueError("x has not been set.")
|
|
314
|
+
|
|
315
|
+
ptr = _nfctlib.jnfct_adjoint_direct(self.plan)
|
|
316
|
+
self.fhat = ptr
|
|
317
|
+
|
|
318
|
+
def nfct_adjoint(self):
|
|
319
|
+
"""
|
|
320
|
+
Alternative call for **nfct_transposed()**
|
|
321
|
+
"""
|
|
322
|
+
return self.nfct_transposed()
|
|
323
|
+
|
|
324
|
+
def nfct_adjoint_direct(self):
|
|
325
|
+
"""
|
|
326
|
+
Alternative call for **nfct_transposed_direct()**
|
|
327
|
+
"""
|
|
328
|
+
return self.nfct_transposed_direct()
|
|
329
|
+
|
|
330
|
+
def adjoint(self):
|
|
331
|
+
"""
|
|
332
|
+
Alternative call for **nfct_adjoint()**
|
|
333
|
+
"""
|
|
334
|
+
return self.nfct_adjoint()
|
|
335
|
+
|
|
336
|
+
def adjoint_direct(self):
|
|
337
|
+
"""
|
|
338
|
+
Alternative call for **nfct_adjoint_direct()**
|
|
339
|
+
"""
|
|
340
|
+
return self.nfct_adjoint_direct()
|
pyNFFT3/NFFT.py
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import numpy as np
|
|
3
|
+
from .flags import *
|
|
4
|
+
from . import _nfftlib
|
|
5
|
+
from . import nfft_plan
|
|
6
|
+
|
|
7
|
+
# Set arugment and return types for functions
|
|
8
|
+
_nfftlib.jnfft_init.argtypes = [
|
|
9
|
+
ctypes.POINTER(nfft_plan),
|
|
10
|
+
ctypes.c_int32,
|
|
11
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
12
|
+
ctypes.c_int32,
|
|
13
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
14
|
+
ctypes.c_int32,
|
|
15
|
+
ctypes.c_uint32,
|
|
16
|
+
ctypes.c_uint32,
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
_nfftlib.jnfft_alloc.restype = ctypes.POINTER(nfft_plan)
|
|
20
|
+
_nfftlib.jnfft_finalize.argtypes = [ctypes.POINTER(nfft_plan)]
|
|
21
|
+
|
|
22
|
+
_nfftlib.jnfft_set_x.argtypes = [
|
|
23
|
+
ctypes.POINTER(nfft_plan),
|
|
24
|
+
np.ctypeslib.ndpointer(np.float64, flags="C"),
|
|
25
|
+
]
|
|
26
|
+
_nfftlib.jnfft_set_f.argtypes = [
|
|
27
|
+
ctypes.POINTER(nfft_plan),
|
|
28
|
+
np.ctypeslib.ndpointer(np.complex128, ndim=1, flags="C"),
|
|
29
|
+
]
|
|
30
|
+
_nfftlib.jnfft_set_fhat.argtypes = [
|
|
31
|
+
ctypes.POINTER(nfft_plan),
|
|
32
|
+
np.ctypeslib.ndpointer(np.complex128, ndim=1, flags="C"),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
_nfftlib.jnfft_trafo.argtypes = [ctypes.POINTER(nfft_plan)]
|
|
36
|
+
_nfftlib.jnfft_adjoint.argtypes = [ctypes.POINTER(nfft_plan)]
|
|
37
|
+
_nfftlib.jnfft_trafo_direct.argtypes = [ctypes.POINTER(nfft_plan)]
|
|
38
|
+
_nfftlib.jnfft_adjoint_direct.argtypes = [ctypes.POINTER(nfft_plan)]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class NFFT:
|
|
42
|
+
"""
|
|
43
|
+
Class to perform non-equispaced fast Fourier transforms (NFFT)
|
|
44
|
+
considering a **D**-dimensional trigonometric polynomial.
|
|
45
|
+
Just **N** and **M** are required for initializing a plan.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, N, M, n=None, m=default_window_cut_off, f1=None, f2=f2_default):
|
|
49
|
+
self.plan = _nfftlib.jnfft_alloc()
|
|
50
|
+
self.N = N # bandwidth tuple
|
|
51
|
+
N_ct = N.ctypes.data_as(ctypes.POINTER(ctypes.c_int32))
|
|
52
|
+
self.M = M # number of nodes
|
|
53
|
+
self.n = n # oversampling per dimension
|
|
54
|
+
self.m = m # window size
|
|
55
|
+
self.D = len(N) # dimensions
|
|
56
|
+
|
|
57
|
+
if any(x <= 0 for x in N):
|
|
58
|
+
raise ValueError(f"Invalid N: {N}. Argument must be a positive integer")
|
|
59
|
+
|
|
60
|
+
if sum(x % 2 for x in N) != 0:
|
|
61
|
+
raise ValueError(f"Invalid N: {N}. Argument must be an even integer")
|
|
62
|
+
|
|
63
|
+
if M <= 0:
|
|
64
|
+
raise ValueError(f"Invalid M: {M}. Argument must be a positive integer")
|
|
65
|
+
|
|
66
|
+
if n is None:
|
|
67
|
+
self.n = (2 ** (np.ceil(np.log(self.N) / np.log(2)) + 1)).astype("int32")
|
|
68
|
+
n_ct = self.n.ctypes.data_as(ctypes.POINTER(ctypes.c_int32))
|
|
69
|
+
|
|
70
|
+
if any(x <= 0 for x in self.n):
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"Invalid n: {self.n}. Argument must be a positive integer"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if any(x <= y for x, y in zip(self.n, N)):
|
|
76
|
+
raise ValueError(f"Invalid n: {self.n}. Argument must fulfil n_i > N_i")
|
|
77
|
+
|
|
78
|
+
if sum(x % 2 for x in self.n) != 0:
|
|
79
|
+
raise ValueError(f"Invalid n: {self.n}. Argument must be an even integer")
|
|
80
|
+
|
|
81
|
+
if m <= 0:
|
|
82
|
+
raise ValueError(f"Invalid m: {m}. Argument must be a positive integer")
|
|
83
|
+
|
|
84
|
+
if f1 is None:
|
|
85
|
+
self.f1 = f1_default if self.D > 1 else f1_default_1d
|
|
86
|
+
else:
|
|
87
|
+
self.f1 = f1
|
|
88
|
+
|
|
89
|
+
self.f2 = f2 # FFTW flags
|
|
90
|
+
_nfftlib.jnfft_init(
|
|
91
|
+
self.plan, self.D, N_ct, self.M, n_ct, self.m, self.f1, self.f2
|
|
92
|
+
)
|
|
93
|
+
self.init_done = True # bool for plan init
|
|
94
|
+
self.finalized = False # bool for finalizer
|
|
95
|
+
self.x = None # nodes, will be set later
|
|
96
|
+
self.f = None # function values
|
|
97
|
+
self.fhat = None # Fourier coefficients
|
|
98
|
+
|
|
99
|
+
def __del__(self):
|
|
100
|
+
self.finalize_plan()
|
|
101
|
+
|
|
102
|
+
def nfft_finalize_plan(self):
|
|
103
|
+
"""
|
|
104
|
+
Finalizes an NFFT plan.
|
|
105
|
+
This function does not have to be called by the user.
|
|
106
|
+
"""
|
|
107
|
+
_nfftlib.jnfft_finalize.argtypes = (ctypes.POINTER(nfft_plan),) # P
|
|
108
|
+
|
|
109
|
+
if not self.init_done:
|
|
110
|
+
raise ValueError("NFFT not initialized.")
|
|
111
|
+
|
|
112
|
+
if not self.finalized:
|
|
113
|
+
self.finalized = True
|
|
114
|
+
_nfftlib.jnfft_finalize(self.plan)
|
|
115
|
+
|
|
116
|
+
def finalize_plan(self):
|
|
117
|
+
"""
|
|
118
|
+
Alternate call for **nfft_finalize_plan()**
|
|
119
|
+
"""
|
|
120
|
+
return self.nfft_finalize_plan()
|
|
121
|
+
|
|
122
|
+
def nfft_init(self):
|
|
123
|
+
"""
|
|
124
|
+
Initializes the NFFT plan in C.
|
|
125
|
+
This function does not have to be called by the user.
|
|
126
|
+
"""
|
|
127
|
+
# Convert N and n to numpy arrays for passing them to C
|
|
128
|
+
Nv = np.array(self.N, dtype=np.int32)
|
|
129
|
+
n = np.array(self.n, dtype=np.int32)
|
|
130
|
+
|
|
131
|
+
# Call init for memory allocation
|
|
132
|
+
ptr = _nfftlib.jnfft_alloc()
|
|
133
|
+
|
|
134
|
+
# Set the pointer
|
|
135
|
+
self.plan = ctypes.cast(ptr, ctypes.POINTER(nfft_plan))
|
|
136
|
+
|
|
137
|
+
# Initialize values
|
|
138
|
+
_nfftlib.jnfft_init(
|
|
139
|
+
self.plan,
|
|
140
|
+
ctypes.c_int(self.D),
|
|
141
|
+
ctypes.cast(Nv.ctypes.data, ctypes.POINTER(ctypes.c_int)),
|
|
142
|
+
ctypes.c_int(self.M),
|
|
143
|
+
ctypes.cast(n.ctypes.data, ctypes.POINTER(ctypes.c_int)),
|
|
144
|
+
ctypes.c_int(self.m),
|
|
145
|
+
ctypes.c_uint(self.f1),
|
|
146
|
+
ctypes.c_uint(self.f2),
|
|
147
|
+
)
|
|
148
|
+
self.init_done = True
|
|
149
|
+
|
|
150
|
+
def init(self):
|
|
151
|
+
"""
|
|
152
|
+
Alternate call for **nfft_init()**
|
|
153
|
+
"""
|
|
154
|
+
return self.nfft_init()
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def x(self):
|
|
158
|
+
return self._X
|
|
159
|
+
|
|
160
|
+
@x.setter
|
|
161
|
+
def x(self, value):
|
|
162
|
+
if value is not None:
|
|
163
|
+
if not (
|
|
164
|
+
isinstance(value, np.ndarray)
|
|
165
|
+
and value.dtype == np.float64
|
|
166
|
+
and value.flags["C"]
|
|
167
|
+
):
|
|
168
|
+
raise RuntimeError("x has to be C-continuous, numpy float64 array")
|
|
169
|
+
if self.D == 1:
|
|
170
|
+
_nfftlib.jnfft_set_x.restype = np.ctypeslib.ndpointer(
|
|
171
|
+
dtype=np.float64, ndim=2, shape=self.M, flags="C"
|
|
172
|
+
)
|
|
173
|
+
else:
|
|
174
|
+
_nfftlib.jnfft_set_x.restype = np.ctypeslib.ndpointer(
|
|
175
|
+
dtype=np.float64, ndim=2, shape=(self.M, self.D), flags="C"
|
|
176
|
+
)
|
|
177
|
+
self._X = _nfftlib.jnfft_set_x(self.plan, value)
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def f(self):
|
|
181
|
+
return self._f
|
|
182
|
+
|
|
183
|
+
@f.setter
|
|
184
|
+
def f(self, value):
|
|
185
|
+
if value is not None:
|
|
186
|
+
if not (
|
|
187
|
+
isinstance(value, np.ndarray)
|
|
188
|
+
and value.dtype == np.complex128
|
|
189
|
+
and value.flags["C"]
|
|
190
|
+
):
|
|
191
|
+
raise RuntimeError("f has to be C-continuous, numpy complex128 array")
|
|
192
|
+
_nfftlib.jnfft_set_f.restype = np.ctypeslib.ndpointer(
|
|
193
|
+
np.complex128, ndim=1, shape=self.M, flags="C"
|
|
194
|
+
)
|
|
195
|
+
self._f = _nfftlib.jnfft_set_f(self.plan, value)
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def fhat(self):
|
|
199
|
+
return self._fhat
|
|
200
|
+
|
|
201
|
+
@fhat.setter
|
|
202
|
+
def fhat(self, value):
|
|
203
|
+
if value is not None:
|
|
204
|
+
if not (
|
|
205
|
+
isinstance(value, np.ndarray)
|
|
206
|
+
and value.dtype == np.complex128
|
|
207
|
+
and value.flags["C"]
|
|
208
|
+
):
|
|
209
|
+
raise RuntimeError(
|
|
210
|
+
"fhat has to be C-continuous, numpy complex128 array"
|
|
211
|
+
)
|
|
212
|
+
Ns = np.prod(self.N)
|
|
213
|
+
_nfftlib.jnfft_set_fhat.restype = np.ctypeslib.ndpointer(
|
|
214
|
+
np.complex128, ndim=1, shape=Ns, flags="C"
|
|
215
|
+
)
|
|
216
|
+
self._fhat = _nfftlib.jnfft_set_fhat(self.plan, value)
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def num_threads(self):
|
|
220
|
+
return _nfftlib.nfft_get_num_threads()
|
|
221
|
+
|
|
222
|
+
def nfft_trafo(self):
|
|
223
|
+
"""
|
|
224
|
+
Computes the NDFT using the fast NFFT algorithm for the provided nodes in **x** and coefficients in **fhat**.
|
|
225
|
+
"""
|
|
226
|
+
_nfftlib.jnfft_trafo.restype = np.ctypeslib.ndpointer(
|
|
227
|
+
np.complex128, shape=self.M, flags="C"
|
|
228
|
+
)
|
|
229
|
+
# Prevent bad stuff from happening
|
|
230
|
+
if self.finalized:
|
|
231
|
+
raise RuntimeError("NFFT already finalized")
|
|
232
|
+
|
|
233
|
+
if not hasattr(self, "fhat"):
|
|
234
|
+
raise ValueError("fhat has not been set.")
|
|
235
|
+
|
|
236
|
+
if not hasattr(self, "x"):
|
|
237
|
+
raise ValueError("x has not been set.")
|
|
238
|
+
|
|
239
|
+
ptr = _nfftlib.jnfft_trafo(self.plan)
|
|
240
|
+
self.f = ptr
|
|
241
|
+
|
|
242
|
+
def trafo(self):
|
|
243
|
+
"""
|
|
244
|
+
Alternative call for **nfft_trafo()**
|
|
245
|
+
"""
|
|
246
|
+
return self.nfft_trafo()
|
|
247
|
+
|
|
248
|
+
def nfft_trafo_direct(self):
|
|
249
|
+
"""
|
|
250
|
+
Computes the NDFT via naive matrix-vector multiplication for the provided nodes in **x** and coefficients in **fhat**.
|
|
251
|
+
"""
|
|
252
|
+
_nfftlib.jnfft_trafo_direct.restype = np.ctypeslib.ndpointer(
|
|
253
|
+
np.complex128, shape=self.M, flags="C"
|
|
254
|
+
)
|
|
255
|
+
# Prevent bad stuff from happening
|
|
256
|
+
if self.finalized:
|
|
257
|
+
raise RuntimeError("NFFT already finalized")
|
|
258
|
+
|
|
259
|
+
if self.fhat is None:
|
|
260
|
+
raise ValueError("fhat has not been set.")
|
|
261
|
+
|
|
262
|
+
if self.x is None:
|
|
263
|
+
raise ValueError("x has not been set.")
|
|
264
|
+
|
|
265
|
+
ptr = _nfftlib.jnfft_trafo_direct(self.plan)
|
|
266
|
+
self.f = ptr
|
|
267
|
+
|
|
268
|
+
def trafo_direct(self):
|
|
269
|
+
"""
|
|
270
|
+
Alternative call for **nfft_trafo_direct()**
|
|
271
|
+
"""
|
|
272
|
+
return self.nfft_trafo_direct()
|
|
273
|
+
|
|
274
|
+
def nfft_adjoint(self):
|
|
275
|
+
"""
|
|
276
|
+
Computes the adjoint NDFT using the fast adjoint NFFT algorithm for the provided nodes in **x** and coefficients in **f**.
|
|
277
|
+
"""
|
|
278
|
+
Ns = np.prod(self.N)
|
|
279
|
+
_nfftlib.jnfft_adjoint.restype = np.ctypeslib.ndpointer(
|
|
280
|
+
np.complex128, shape=Ns, flags="C"
|
|
281
|
+
)
|
|
282
|
+
# Prevent bad stuff from happening
|
|
283
|
+
if self.finalized:
|
|
284
|
+
raise RuntimeError("NFFT already finalized")
|
|
285
|
+
|
|
286
|
+
if not hasattr(self, "f"):
|
|
287
|
+
raise ValueError("f has not been set.")
|
|
288
|
+
|
|
289
|
+
if not hasattr(self, "x"):
|
|
290
|
+
raise ValueError("x has not been set.")
|
|
291
|
+
|
|
292
|
+
ptr = _nfftlib.jnfft_adjoint(self.plan)
|
|
293
|
+
self.fhat = ptr
|
|
294
|
+
|
|
295
|
+
def adjoint(self):
|
|
296
|
+
"""
|
|
297
|
+
Alternative call for **nfft_adjoint()**
|
|
298
|
+
"""
|
|
299
|
+
return self.nfft_adjoint()
|
|
300
|
+
|
|
301
|
+
def nfft_adjoint_direct(self):
|
|
302
|
+
"""
|
|
303
|
+
Computes the adjoint NDFT using naive matrix-vector multiplication for the provided nodes in **x** and coefficients in **f**.
|
|
304
|
+
"""
|
|
305
|
+
Ns = np.prod(self.N)
|
|
306
|
+
_nfftlib.jnfft_adjoint_direct.restype = np.ctypeslib.ndpointer(
|
|
307
|
+
np.complex128, shape=Ns, flags="C"
|
|
308
|
+
)
|
|
309
|
+
# Prevent bad stuff from happening
|
|
310
|
+
if self.finalized:
|
|
311
|
+
raise RuntimeError("NFFT already finalized")
|
|
312
|
+
|
|
313
|
+
if not hasattr(self, "f"):
|
|
314
|
+
raise ValueError("f has not been set.")
|
|
315
|
+
|
|
316
|
+
if not hasattr(self, "x"):
|
|
317
|
+
raise ValueError("x has not been set.")
|
|
318
|
+
|
|
319
|
+
ptr = _nfftlib.jnfft_adjoint_direct(self.plan)
|
|
320
|
+
self.fhat = ptr
|
|
321
|
+
|
|
322
|
+
def adjoint_direct(self):
|
|
323
|
+
"""
|
|
324
|
+
Alternative call for **nfft_adjoint_direct()**
|
|
325
|
+
"""
|
|
326
|
+
return self.nfft_adjoint_direct()
|
pyNFFT3/NFMT.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# TODO: Add Python equivalent of NFMT.jl
|