gpuarray 0.3.0__py3-none-any.whl → 0.4.0__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.
- gpuarray/__init__.py +8 -0
- gpuarray/api.py +35 -1
- gpuarray/core.py +218 -0
- gpuarray/shaders.py +207 -0
- {gpuarray-0.3.0.dist-info → gpuarray-0.4.0.dist-info}/METADATA +1 -1
- gpuarray-0.4.0.dist-info/RECORD +9 -0
- gpuarray-0.3.0.dist-info/RECORD +0 -9
- {gpuarray-0.3.0.dist-info → gpuarray-0.4.0.dist-info}/WHEEL +0 -0
- {gpuarray-0.3.0.dist-info → gpuarray-0.4.0.dist-info}/top_level.txt +0 -0
gpuarray/__init__.py
CHANGED
|
@@ -27,6 +27,10 @@ from .api import (
|
|
|
27
27
|
concatenate,
|
|
28
28
|
stack,
|
|
29
29
|
random,
|
|
30
|
+
fft,
|
|
31
|
+
ifft,
|
|
32
|
+
fft_freq,
|
|
33
|
+
fft_magnitude,
|
|
30
34
|
)
|
|
31
35
|
|
|
32
36
|
__version__ = "0.1.0"
|
|
@@ -58,4 +62,8 @@ __all__ = [
|
|
|
58
62
|
"concatenate",
|
|
59
63
|
"stack",
|
|
60
64
|
"random",
|
|
65
|
+
"fft",
|
|
66
|
+
"ifft",
|
|
67
|
+
"fft_freq",
|
|
68
|
+
"fft_magnitude",
|
|
61
69
|
]
|
gpuarray/api.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import Sequence
|
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
|
-
from .core import GPUArray, _from_numpy, matmul as _matmul
|
|
9
|
+
from .core import GPUArray, _from_numpy, matmul as _matmul, _complex_magnitude
|
|
10
10
|
from . import shaders
|
|
11
11
|
|
|
12
12
|
|
|
@@ -180,3 +180,37 @@ class _Random:
|
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
random = _Random()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# ----- FFT functions -----
|
|
186
|
+
|
|
187
|
+
def fft(a) -> GPUArray:
|
|
188
|
+
"""1D FFT. Input is real or complex (interleaved). Returns complex GPUArray."""
|
|
189
|
+
if not isinstance(a, GPUArray):
|
|
190
|
+
a = array(a)
|
|
191
|
+
return a.fft()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def ifft(a) -> GPUArray:
|
|
195
|
+
"""1D inverse FFT. Input is complex interleaved. Returns complex GPUArray."""
|
|
196
|
+
if not isinstance(a, GPUArray):
|
|
197
|
+
a = array(a)
|
|
198
|
+
return a.ifft()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def fft_freq(n: int, d: float = 1.0) -> GPUArray:
|
|
202
|
+
"""Return the DFT sample frequencies (like numpy.fft.fftfreq)."""
|
|
203
|
+
results = np.empty(n, dtype=np.float32)
|
|
204
|
+
N = (n - 1) // 2 + 1
|
|
205
|
+
results[:N] = np.arange(0, N, dtype=np.float32)
|
|
206
|
+
results[N:] = np.arange(-(n // 2), 0, dtype=np.float32)
|
|
207
|
+
results /= (n * d)
|
|
208
|
+
return _from_numpy(results)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def fft_magnitude(a) -> GPUArray:
|
|
212
|
+
"""Compute magnitude spectrum |FFT(x)|. Returns real GPUArray."""
|
|
213
|
+
if not isinstance(a, GPUArray):
|
|
214
|
+
a = array(a)
|
|
215
|
+
fft_result = a.fft()
|
|
216
|
+
return _complex_magnitude(fft_result)
|
gpuarray/core.py
CHANGED
|
@@ -658,6 +658,224 @@ class GPUArray:
|
|
|
658
658
|
return self._shape[0]
|
|
659
659
|
|
|
660
660
|
|
|
661
|
+
# ----- FFT methods -----
|
|
662
|
+
|
|
663
|
+
def fft(self) -> "GPUArray":
|
|
664
|
+
"""1D FFT. Input is real or complex (interleaved). Returns complex GPUArray (interleaved)."""
|
|
665
|
+
return _fft_impl(self, inverse=False)
|
|
666
|
+
|
|
667
|
+
def ifft(self) -> "GPUArray":
|
|
668
|
+
"""1D inverse FFT. Input is complex interleaved. Returns complex GPUArray."""
|
|
669
|
+
return _fft_impl(self, inverse=True)
|
|
670
|
+
|
|
671
|
+
def fft_magnitude(self) -> "GPUArray":
|
|
672
|
+
"""Compute magnitude spectrum |FFT(x)|. Returns real GPUArray."""
|
|
673
|
+
fft_result = self.fft()
|
|
674
|
+
return _complex_magnitude(fft_result)
|
|
675
|
+
|
|
676
|
+
def fft_phase(self) -> "GPUArray":
|
|
677
|
+
"""Compute phase spectrum angle(FFT(x)). Returns real GPUArray."""
|
|
678
|
+
fft_result = self.fft()
|
|
679
|
+
return _complex_phase(fft_result)
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def _next_power_of_2(n: int) -> int:
|
|
683
|
+
"""Return the smallest power of 2 >= n."""
|
|
684
|
+
if n <= 1:
|
|
685
|
+
return 1
|
|
686
|
+
p = 1
|
|
687
|
+
while p < n:
|
|
688
|
+
p <<= 1
|
|
689
|
+
return p
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
def _fft_impl(arr: GPUArray, inverse: bool = False) -> GPUArray:
|
|
693
|
+
"""Cooley-Tukey radix-2 FFT/IFFT on GPU."""
|
|
694
|
+
device = get_device()
|
|
695
|
+
|
|
696
|
+
# Determine if input is real or complex (interleaved)
|
|
697
|
+
# Real: 1D array of N floats -> N-point FFT
|
|
698
|
+
# Complex interleaved: 1D array of 2*N floats -> N-point FFT
|
|
699
|
+
# We treat it as real if ndim==1 and size is the number of points
|
|
700
|
+
# For simplicity: if the array was produced by a previous FFT (even size),
|
|
701
|
+
# the caller should know. We'll check: if doing IFFT, input must be complex.
|
|
702
|
+
|
|
703
|
+
flat = arr.to_numpy().flatten()
|
|
704
|
+
|
|
705
|
+
if inverse:
|
|
706
|
+
# Input must be complex interleaved (even number of floats)
|
|
707
|
+
assert len(flat) % 2 == 0, "IFFT input must be complex interleaved (even length)"
|
|
708
|
+
N_complex = len(flat) // 2
|
|
709
|
+
else:
|
|
710
|
+
# Assume real input -> convert to complex
|
|
711
|
+
N_complex = len(flat)
|
|
712
|
+
|
|
713
|
+
# Pad to next power of 2
|
|
714
|
+
N = _next_power_of_2(N_complex)
|
|
715
|
+
log2N = int(math.log2(N))
|
|
716
|
+
|
|
717
|
+
if inverse:
|
|
718
|
+
# Pad complex data
|
|
719
|
+
if N > N_complex:
|
|
720
|
+
padded = np.zeros(2 * N, dtype=np.float32)
|
|
721
|
+
padded[:len(flat)] = flat
|
|
722
|
+
else:
|
|
723
|
+
padded = flat.copy()
|
|
724
|
+
complex_data = padded
|
|
725
|
+
else:
|
|
726
|
+
# Convert real to complex interleaved
|
|
727
|
+
padded_real = np.zeros(N, dtype=np.float32)
|
|
728
|
+
padded_real[:len(flat)] = flat
|
|
729
|
+
complex_data = np.zeros(2 * N, dtype=np.float32)
|
|
730
|
+
complex_data[0::2] = padded_real # real parts
|
|
731
|
+
# imaginary parts are already 0
|
|
732
|
+
|
|
733
|
+
# Upload complex data to GPU
|
|
734
|
+
input_buf = device.create_buffer_with_data(
|
|
735
|
+
data=complex_data.tobytes(),
|
|
736
|
+
usage=wgpu.BufferUsage.STORAGE | wgpu.BufferUsage.COPY_SRC | wgpu.BufferUsage.COPY_DST,
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
# Step 1: Bit-reversal permutation
|
|
740
|
+
output_buf = device.create_buffer(
|
|
741
|
+
size=2 * N * 4,
|
|
742
|
+
usage=wgpu.BufferUsage.STORAGE | wgpu.BufferUsage.COPY_SRC | wgpu.BufferUsage.COPY_DST,
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
_, br_pipeline = _get_pipeline(shaders.FFT_BIT_REVERSE_SHADER)
|
|
746
|
+
params_data = struct.pack("IIII", N, log2N, 0, 0)
|
|
747
|
+
params_buf = device.create_buffer_with_data(
|
|
748
|
+
data=params_data, usage=wgpu.BufferUsage.UNIFORM,
|
|
749
|
+
)
|
|
750
|
+
bind_group = device.create_bind_group(
|
|
751
|
+
layout=br_pipeline.get_bind_group_layout(0),
|
|
752
|
+
entries=[
|
|
753
|
+
{"binding": 0, "resource": {"buffer": input_buf, "offset": 0, "size": 2 * N * 4}},
|
|
754
|
+
{"binding": 1, "resource": {"buffer": output_buf, "offset": 0, "size": 2 * N * 4}},
|
|
755
|
+
{"binding": 2, "resource": {"buffer": params_buf, "offset": 0, "size": 16}},
|
|
756
|
+
],
|
|
757
|
+
)
|
|
758
|
+
n_workgroups = math.ceil(N / WORKGROUP_SIZE)
|
|
759
|
+
encoder = device.create_command_encoder()
|
|
760
|
+
pass_enc = encoder.begin_compute_pass()
|
|
761
|
+
pass_enc.set_pipeline(br_pipeline)
|
|
762
|
+
pass_enc.set_bind_group(0, bind_group)
|
|
763
|
+
pass_enc.dispatch_workgroups(n_workgroups)
|
|
764
|
+
pass_enc.end()
|
|
765
|
+
device.queue.submit([encoder.finish()])
|
|
766
|
+
input_buf.destroy()
|
|
767
|
+
params_buf.destroy()
|
|
768
|
+
|
|
769
|
+
# Step 2: Butterfly stages
|
|
770
|
+
butterfly_shader = shaders.IFFT_BUTTERFLY_SHADER if inverse else shaders.FFT_BUTTERFLY_SHADER
|
|
771
|
+
_, bf_pipeline = _get_pipeline(butterfly_shader)
|
|
772
|
+
|
|
773
|
+
for stage in range(log2N):
|
|
774
|
+
params_data = struct.pack("IIII", N, stage, 0, 0)
|
|
775
|
+
params_buf = device.create_buffer_with_data(
|
|
776
|
+
data=params_data, usage=wgpu.BufferUsage.UNIFORM,
|
|
777
|
+
)
|
|
778
|
+
bind_group = device.create_bind_group(
|
|
779
|
+
layout=bf_pipeline.get_bind_group_layout(0),
|
|
780
|
+
entries=[
|
|
781
|
+
{"binding": 0, "resource": {"buffer": output_buf, "offset": 0, "size": 2 * N * 4}},
|
|
782
|
+
{"binding": 1, "resource": {"buffer": params_buf, "offset": 0, "size": 16}},
|
|
783
|
+
],
|
|
784
|
+
)
|
|
785
|
+
n_workgroups_bf = math.ceil(N // 2 / WORKGROUP_SIZE)
|
|
786
|
+
if n_workgroups_bf == 0:
|
|
787
|
+
n_workgroups_bf = 1
|
|
788
|
+
encoder = device.create_command_encoder()
|
|
789
|
+
pass_enc = encoder.begin_compute_pass()
|
|
790
|
+
pass_enc.set_pipeline(bf_pipeline)
|
|
791
|
+
pass_enc.set_bind_group(0, bind_group)
|
|
792
|
+
pass_enc.dispatch_workgroups(n_workgroups_bf)
|
|
793
|
+
pass_enc.end()
|
|
794
|
+
device.queue.submit([encoder.finish()])
|
|
795
|
+
params_buf.destroy()
|
|
796
|
+
|
|
797
|
+
# Step 3: For IFFT, scale by 1/N
|
|
798
|
+
if inverse:
|
|
799
|
+
_, scale_pipeline = _get_pipeline(shaders.FFT_SCALE_SHADER)
|
|
800
|
+
scale_val = 1.0 / float(N)
|
|
801
|
+
params_data = struct.pack("f", scale_val) + b"\x00" * 12
|
|
802
|
+
params_buf = device.create_buffer_with_data(
|
|
803
|
+
data=params_data, usage=wgpu.BufferUsage.UNIFORM,
|
|
804
|
+
)
|
|
805
|
+
bind_group = device.create_bind_group(
|
|
806
|
+
layout=scale_pipeline.get_bind_group_layout(0),
|
|
807
|
+
entries=[
|
|
808
|
+
{"binding": 0, "resource": {"buffer": output_buf, "offset": 0, "size": 2 * N * 4}},
|
|
809
|
+
{"binding": 1, "resource": {"buffer": params_buf, "offset": 0, "size": 16}},
|
|
810
|
+
],
|
|
811
|
+
)
|
|
812
|
+
n_workgroups_sc = math.ceil(2 * N / WORKGROUP_SIZE)
|
|
813
|
+
encoder = device.create_command_encoder()
|
|
814
|
+
pass_enc = encoder.begin_compute_pass()
|
|
815
|
+
pass_enc.set_pipeline(scale_pipeline)
|
|
816
|
+
pass_enc.set_bind_group(0, bind_group)
|
|
817
|
+
pass_enc.dispatch_workgroups(n_workgroups_sc)
|
|
818
|
+
pass_enc.end()
|
|
819
|
+
device.queue.submit([encoder.finish()])
|
|
820
|
+
params_buf.destroy()
|
|
821
|
+
|
|
822
|
+
return GPUArray(output_buf, (2 * N,))
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def _complex_magnitude(arr: GPUArray) -> GPUArray:
|
|
826
|
+
"""Compute magnitude of complex interleaved array."""
|
|
827
|
+
device = get_device()
|
|
828
|
+
N = arr.size // 2
|
|
829
|
+
_, pipeline = _get_pipeline(shaders.COMPLEX_MAGNITUDE_SHADER)
|
|
830
|
+
out_buf = device.create_buffer(
|
|
831
|
+
size=N * 4,
|
|
832
|
+
usage=wgpu.BufferUsage.STORAGE | wgpu.BufferUsage.COPY_SRC | wgpu.BufferUsage.COPY_DST,
|
|
833
|
+
)
|
|
834
|
+
bind_group = device.create_bind_group(
|
|
835
|
+
layout=pipeline.get_bind_group_layout(0),
|
|
836
|
+
entries=[
|
|
837
|
+
{"binding": 0, "resource": {"buffer": arr._buffer, "offset": 0, "size": arr.size * 4}},
|
|
838
|
+
{"binding": 1, "resource": {"buffer": out_buf, "offset": 0, "size": N * 4}},
|
|
839
|
+
],
|
|
840
|
+
)
|
|
841
|
+
n_workgroups = math.ceil(N / WORKGROUP_SIZE)
|
|
842
|
+
encoder = device.create_command_encoder()
|
|
843
|
+
pass_enc = encoder.begin_compute_pass()
|
|
844
|
+
pass_enc.set_pipeline(pipeline)
|
|
845
|
+
pass_enc.set_bind_group(0, bind_group)
|
|
846
|
+
pass_enc.dispatch_workgroups(n_workgroups)
|
|
847
|
+
pass_enc.end()
|
|
848
|
+
device.queue.submit([encoder.finish()])
|
|
849
|
+
return GPUArray(out_buf, (N,))
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def _complex_phase(arr: GPUArray) -> GPUArray:
|
|
853
|
+
"""Compute phase of complex interleaved array."""
|
|
854
|
+
device = get_device()
|
|
855
|
+
N = arr.size // 2
|
|
856
|
+
_, pipeline = _get_pipeline(shaders.COMPLEX_PHASE_SHADER)
|
|
857
|
+
out_buf = device.create_buffer(
|
|
858
|
+
size=N * 4,
|
|
859
|
+
usage=wgpu.BufferUsage.STORAGE | wgpu.BufferUsage.COPY_SRC | wgpu.BufferUsage.COPY_DST,
|
|
860
|
+
)
|
|
861
|
+
bind_group = device.create_bind_group(
|
|
862
|
+
layout=pipeline.get_bind_group_layout(0),
|
|
863
|
+
entries=[
|
|
864
|
+
{"binding": 0, "resource": {"buffer": arr._buffer, "offset": 0, "size": arr.size * 4}},
|
|
865
|
+
{"binding": 1, "resource": {"buffer": out_buf, "offset": 0, "size": N * 4}},
|
|
866
|
+
],
|
|
867
|
+
)
|
|
868
|
+
n_workgroups = math.ceil(N / WORKGROUP_SIZE)
|
|
869
|
+
encoder = device.create_command_encoder()
|
|
870
|
+
pass_enc = encoder.begin_compute_pass()
|
|
871
|
+
pass_enc.set_pipeline(pipeline)
|
|
872
|
+
pass_enc.set_bind_group(0, bind_group)
|
|
873
|
+
pass_enc.dispatch_workgroups(n_workgroups)
|
|
874
|
+
pass_enc.end()
|
|
875
|
+
device.queue.submit([encoder.finish()])
|
|
876
|
+
return GPUArray(out_buf, (N,))
|
|
877
|
+
|
|
878
|
+
|
|
661
879
|
def _from_numpy(data: np.ndarray) -> GPUArray:
|
|
662
880
|
"""Create a GPUArray from a numpy array."""
|
|
663
881
|
device = get_device()
|
gpuarray/shaders.py
CHANGED
|
@@ -879,3 +879,210 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
|
879
879
|
if (i < arrayLength(&a)) { result[i] = a[i] / b[i / dims.cols]; }
|
|
880
880
|
}
|
|
881
881
|
"""
|
|
882
|
+
|
|
883
|
+
# ---------------------------------------------------------------------------
|
|
884
|
+
# FFT shaders (Cooley-Tukey radix-2)
|
|
885
|
+
# Complex numbers stored as interleaved float32 pairs: [re0, im0, re1, im1, ...]
|
|
886
|
+
# ---------------------------------------------------------------------------
|
|
887
|
+
|
|
888
|
+
REAL_TO_COMPLEX_SHADER = """
|
|
889
|
+
@group(0) @binding(0) var<storage, read> input: array<f32>;
|
|
890
|
+
@group(0) @binding(1) var<storage, read_write> output: array<f32>;
|
|
891
|
+
|
|
892
|
+
@compute @workgroup_size(256)
|
|
893
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
894
|
+
let i = gid.x;
|
|
895
|
+
let n = arrayLength(&input);
|
|
896
|
+
if (i < n) {
|
|
897
|
+
output[i * 2u] = input[i];
|
|
898
|
+
output[i * 2u + 1u] = 0.0;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
"""
|
|
902
|
+
|
|
903
|
+
FFT_BIT_REVERSE_SHADER = """
|
|
904
|
+
struct Params {
|
|
905
|
+
N: u32,
|
|
906
|
+
log2N: u32,
|
|
907
|
+
_pad1: u32,
|
|
908
|
+
_pad2: u32,
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
@group(0) @binding(0) var<storage, read> input: array<f32>;
|
|
912
|
+
@group(0) @binding(1) var<storage, read_write> output: array<f32>;
|
|
913
|
+
@group(0) @binding(2) var<uniform> params: Params;
|
|
914
|
+
|
|
915
|
+
fn bit_reverse(x: u32, bits: u32) -> u32 {
|
|
916
|
+
var v = x;
|
|
917
|
+
var r: u32 = 0u;
|
|
918
|
+
for (var i: u32 = 0u; i < bits; i = i + 1u) {
|
|
919
|
+
r = (r << 1u) | (v & 1u);
|
|
920
|
+
v = v >> 1u;
|
|
921
|
+
}
|
|
922
|
+
return r;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
@compute @workgroup_size(256)
|
|
926
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
927
|
+
let i = gid.x;
|
|
928
|
+
if (i >= params.N) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
let j = bit_reverse(i, params.log2N);
|
|
932
|
+
output[j * 2u] = input[i * 2u];
|
|
933
|
+
output[j * 2u + 1u] = input[i * 2u + 1u];
|
|
934
|
+
}
|
|
935
|
+
"""
|
|
936
|
+
|
|
937
|
+
FFT_BUTTERFLY_SHADER = """
|
|
938
|
+
struct Params {
|
|
939
|
+
N: u32,
|
|
940
|
+
stage: u32,
|
|
941
|
+
_pad1: u32,
|
|
942
|
+
_pad2: u32,
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
|
|
946
|
+
@group(0) @binding(1) var<uniform> params: Params;
|
|
947
|
+
|
|
948
|
+
@compute @workgroup_size(256)
|
|
949
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
950
|
+
let i = gid.x;
|
|
951
|
+
let m = 1u << (params.stage + 1u);
|
|
952
|
+
let half = m >> 1u;
|
|
953
|
+
|
|
954
|
+
// Total number of butterflies is N/2
|
|
955
|
+
if (i >= params.N / 2u) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// Which butterfly group and position within group
|
|
960
|
+
let group = i / half;
|
|
961
|
+
let pos = i % half;
|
|
962
|
+
let j = group * m + pos;
|
|
963
|
+
let k = j + half;
|
|
964
|
+
|
|
965
|
+
let pi = 3.14159265358979323846;
|
|
966
|
+
let angle = -2.0 * pi * f32(pos) / f32(m);
|
|
967
|
+
let tw_re = cos(angle);
|
|
968
|
+
let tw_im = sin(angle);
|
|
969
|
+
|
|
970
|
+
// Load X[k]
|
|
971
|
+
let xk_re = data[k * 2u];
|
|
972
|
+
let xk_im = data[k * 2u + 1u];
|
|
973
|
+
|
|
974
|
+
// Twiddle multiply: W * X[k]
|
|
975
|
+
let t_re = tw_re * xk_re - tw_im * xk_im;
|
|
976
|
+
let t_im = tw_re * xk_im + tw_im * xk_re;
|
|
977
|
+
|
|
978
|
+
// Load X[j]
|
|
979
|
+
let xj_re = data[j * 2u];
|
|
980
|
+
let xj_im = data[j * 2u + 1u];
|
|
981
|
+
|
|
982
|
+
// Butterfly
|
|
983
|
+
data[j * 2u] = xj_re + t_re;
|
|
984
|
+
data[j * 2u + 1u] = xj_im + t_im;
|
|
985
|
+
data[k * 2u] = xj_re - t_re;
|
|
986
|
+
data[k * 2u + 1u] = xj_im - t_im;
|
|
987
|
+
}
|
|
988
|
+
"""
|
|
989
|
+
|
|
990
|
+
IFFT_BUTTERFLY_SHADER = """
|
|
991
|
+
struct Params {
|
|
992
|
+
N: u32,
|
|
993
|
+
stage: u32,
|
|
994
|
+
_pad1: u32,
|
|
995
|
+
_pad2: u32,
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
|
|
999
|
+
@group(0) @binding(1) var<uniform> params: Params;
|
|
1000
|
+
|
|
1001
|
+
@compute @workgroup_size(256)
|
|
1002
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
1003
|
+
let i = gid.x;
|
|
1004
|
+
let m = 1u << (params.stage + 1u);
|
|
1005
|
+
let half = m >> 1u;
|
|
1006
|
+
|
|
1007
|
+
if (i >= params.N / 2u) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
let group = i / half;
|
|
1012
|
+
let pos = i % half;
|
|
1013
|
+
let j = group * m + pos;
|
|
1014
|
+
let k = j + half;
|
|
1015
|
+
|
|
1016
|
+
let pi = 3.14159265358979323846;
|
|
1017
|
+
// Conjugate twiddle for IFFT: positive angle
|
|
1018
|
+
let angle = 2.0 * pi * f32(pos) / f32(m);
|
|
1019
|
+
let tw_re = cos(angle);
|
|
1020
|
+
let tw_im = sin(angle);
|
|
1021
|
+
|
|
1022
|
+
let xk_re = data[k * 2u];
|
|
1023
|
+
let xk_im = data[k * 2u + 1u];
|
|
1024
|
+
|
|
1025
|
+
let t_re = tw_re * xk_re - tw_im * xk_im;
|
|
1026
|
+
let t_im = tw_re * xk_im + tw_im * xk_re;
|
|
1027
|
+
|
|
1028
|
+
let xj_re = data[j * 2u];
|
|
1029
|
+
let xj_im = data[j * 2u + 1u];
|
|
1030
|
+
|
|
1031
|
+
data[j * 2u] = xj_re + t_re;
|
|
1032
|
+
data[j * 2u + 1u] = xj_im + t_im;
|
|
1033
|
+
data[k * 2u] = xj_re - t_re;
|
|
1034
|
+
data[k * 2u + 1u] = xj_im - t_im;
|
|
1035
|
+
}
|
|
1036
|
+
"""
|
|
1037
|
+
|
|
1038
|
+
FFT_SCALE_SHADER = """
|
|
1039
|
+
struct Params {
|
|
1040
|
+
scalar: f32,
|
|
1041
|
+
_pad1: u32,
|
|
1042
|
+
_pad2: u32,
|
|
1043
|
+
_pad3: u32,
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
|
|
1047
|
+
@group(0) @binding(1) var<uniform> params: Params;
|
|
1048
|
+
|
|
1049
|
+
@compute @workgroup_size(256)
|
|
1050
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
1051
|
+
let i = gid.x;
|
|
1052
|
+
if (i < arrayLength(&data)) {
|
|
1053
|
+
data[i] = data[i] * params.scalar;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
"""
|
|
1057
|
+
|
|
1058
|
+
COMPLEX_MAGNITUDE_SHADER = """
|
|
1059
|
+
@group(0) @binding(0) var<storage, read> input: array<f32>;
|
|
1060
|
+
@group(0) @binding(1) var<storage, read_write> output: array<f32>;
|
|
1061
|
+
|
|
1062
|
+
@compute @workgroup_size(256)
|
|
1063
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
1064
|
+
let i = gid.x;
|
|
1065
|
+
let n = arrayLength(&output);
|
|
1066
|
+
if (i < n) {
|
|
1067
|
+
let re = input[i * 2u];
|
|
1068
|
+
let im = input[i * 2u + 1u];
|
|
1069
|
+
output[i] = sqrt(re * re + im * im);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
"""
|
|
1073
|
+
|
|
1074
|
+
COMPLEX_PHASE_SHADER = """
|
|
1075
|
+
@group(0) @binding(0) var<storage, read> input: array<f32>;
|
|
1076
|
+
@group(0) @binding(1) var<storage, read_write> output: array<f32>;
|
|
1077
|
+
|
|
1078
|
+
@compute @workgroup_size(256)
|
|
1079
|
+
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
1080
|
+
let i = gid.x;
|
|
1081
|
+
let n = arrayLength(&output);
|
|
1082
|
+
if (i < n) {
|
|
1083
|
+
let re = input[i * 2u];
|
|
1084
|
+
let im = input[i * 2u + 1u];
|
|
1085
|
+
output[i] = atan2(im, re);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
"""
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
gpuarray/__init__.py,sha256=YmH2uwL9iez8AUGDE2OCY6sVNIrC49FNsUP5PFxrNmk,864
|
|
2
|
+
gpuarray/api.py,sha256=wiZnz4rs631nGiR_wW01Rr1BNgT0RL8fD2rMRQ7SSpg,5883
|
|
3
|
+
gpuarray/core.py,sha256=D2Q5Lwe8p2PmQjaUqMYEWd2qDSQkI1xt5IfTZ3MyIFY,39048
|
|
4
|
+
gpuarray/device.py,sha256=q80VOrKhy1u5i4AMLUKhWVBwFqQjoJ4tNBm234Wpf-A,452
|
|
5
|
+
gpuarray/shaders.py,sha256=0-71ErmWkBCHqPoROsORiVRgl48c4BsoqAjSLOn2QKg,30566
|
|
6
|
+
gpuarray-0.4.0.dist-info/METADATA,sha256=GRL-qds44XKfiHArZsRivg-WEVWondmQtOEXA0tgofI,3132
|
|
7
|
+
gpuarray-0.4.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
8
|
+
gpuarray-0.4.0.dist-info/top_level.txt,sha256=iruYli4LmPBqhaZxTVvzT1bT0VeN6dbC0GE29lT1CbI,9
|
|
9
|
+
gpuarray-0.4.0.dist-info/RECORD,,
|
gpuarray-0.3.0.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
gpuarray/__init__.py,sha256=MB9k9oXPvA6wRCCZii72HtpRxjXPtkiT2RaJsiVY-wY,752
|
|
2
|
-
gpuarray/api.py,sha256=PlYqtvY7jEV3xIneGdRoqXJjVOP3w2fnHyZgVP6fqis,4874
|
|
3
|
-
gpuarray/core.py,sha256=o7UymuEJoyYQAKao4O4wRtFw911Ut3h7tnQJOT6N0BU,30676
|
|
4
|
-
gpuarray/device.py,sha256=q80VOrKhy1u5i4AMLUKhWVBwFqQjoJ4tNBm234Wpf-A,452
|
|
5
|
-
gpuarray/shaders.py,sha256=oG7vd3iqO1eJ7UMIZx-46UPcb9gNVUWhBa6q8iKOSxo,25467
|
|
6
|
-
gpuarray-0.3.0.dist-info/METADATA,sha256=XrCwPw9lCTIHb5qMSZjHypALq5BLwwHjrV2SVoLDnhE,3132
|
|
7
|
-
gpuarray-0.3.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
8
|
-
gpuarray-0.3.0.dist-info/top_level.txt,sha256=iruYli4LmPBqhaZxTVvzT1bT0VeN6dbC0GE29lT1CbI,9
|
|
9
|
-
gpuarray-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|