numo-pocketfft 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ /*
2
+ * This file is part of pocketfft.
3
+ * Licensed under a 3-clause BSD style license - see LICENSE.md
4
+ */
5
+
6
+ /*! \file pocketfft.h
7
+ * Public interface of the pocketfft library
8
+ *
9
+ * Copyright (C) 2008-2018 Max-Planck-Society
10
+ * \author Martin Reinecke
11
+ */
12
+
13
+ #ifndef POCKETFFT_H
14
+ #define POCKETFFT_H
15
+
16
+ #include <stdlib.h>
17
+
18
+ struct cfft_plan_i;
19
+ typedef struct cfft_plan_i * cfft_plan;
20
+ cfft_plan make_cfft_plan (size_t length);
21
+ void destroy_cfft_plan (cfft_plan plan);
22
+ int cfft_backward(cfft_plan plan, double c[], double fct);
23
+ int cfft_forward(cfft_plan plan, double c[], double fct);
24
+ size_t cfft_length(cfft_plan plan);
25
+
26
+ struct rfft_plan_i;
27
+ typedef struct rfft_plan_i * rfft_plan;
28
+ rfft_plan make_rfft_plan (size_t length);
29
+ void destroy_rfft_plan (rfft_plan plan);
30
+ int rfft_backward(rfft_plan plan, double c[], double fct);
31
+ int rfft_forward(rfft_plan plan, double c[], double fct);
32
+ size_t rfft_length(rfft_plan plan);
33
+
34
+ #endif
@@ -0,0 +1,214 @@
1
+ #include "pocketfftext.h"
2
+
3
+ VALUE mNumo;
4
+ VALUE mPocketfft;
5
+
6
+ VALUE numo_pocketfft_fft(VALUE x_val, int is_forward)
7
+ {
8
+ narray_t* x_nary;
9
+ double* x_pt;
10
+ size_t length;
11
+ int n_dims;
12
+ int n_repeats;
13
+ int i;
14
+ int res;
15
+ double fct;
16
+ VALUE z_val;
17
+ double* z_pt;
18
+ narray_t* z_nary;
19
+ cfft_plan plan = NULL;
20
+
21
+ if (CLASS_OF(x_val) != numo_cDComplex) {
22
+ x_val = rb_funcall(numo_cDComplex, rb_intern("cast"), 1, x_val);
23
+ }
24
+ if (!RTEST(nary_check_contiguous(x_val))) {
25
+ x_val = nary_dup(x_val);
26
+ }
27
+
28
+ GetNArray(x_val, x_nary);
29
+ n_dims = NA_NDIM(x_nary);
30
+ length = NA_SHAPE(x_nary)[n_dims - 1];
31
+ x_pt = (double*)na_get_pointer_for_read(x_val);
32
+
33
+ z_val = nary_s_new_like(numo_cDComplex, x_val);
34
+ z_pt = (double*)na_get_pointer_for_write(z_val);
35
+ GetNArray(z_val, z_nary);
36
+ for (i = 0; i < (int)(NA_SIZE(z_nary) * 2); z_pt[i++] = 0.0);
37
+
38
+ fct = is_forward == 1 ? 1.0 : 1.0 / length;
39
+ plan = make_cfft_plan(length);
40
+ if (!plan) {
41
+ return Qnil;
42
+ }
43
+
44
+ n_repeats = (int)(NA_SIZE(x_nary)) / length;
45
+ for (i = 0; i < n_repeats; i++) {
46
+ memcpy(z_pt, x_pt, 2 * length * sizeof(double));
47
+ res = is_forward == 1 ? cfft_forward(plan, z_pt, fct) : cfft_backward(plan, z_pt, fct);
48
+ if (res != 0) { break; }
49
+ z_pt += length * 2;
50
+ x_pt += length * 2;
51
+ }
52
+
53
+ if (plan) {
54
+ destroy_cfft_plan(plan);
55
+ }
56
+
57
+ return z_val;
58
+ }
59
+
60
+ /**
61
+ * @!visibility private
62
+ */
63
+ static VALUE numo_pocketfft_cfft(VALUE self, VALUE x_val)
64
+ {
65
+ return numo_pocketfft_fft(x_val, 1);
66
+ }
67
+
68
+ /**
69
+ * @!visibility private
70
+ */
71
+ static VALUE numo_pocketfft_icfft(VALUE self, VALUE x_val)
72
+ {
73
+ return numo_pocketfft_fft(x_val, 0);
74
+ }
75
+
76
+ /**
77
+ * @!visibility private
78
+ */
79
+ static VALUE numo_pocketfft_rfft(VALUE self, VALUE x_val)
80
+ {
81
+ narray_t* x_nary;
82
+ double* x_pt;
83
+ int n_dims;
84
+ size_t length;
85
+ int n_repeats;
86
+ int i;
87
+ size_t* z_shape;
88
+ VALUE z_val;
89
+ narray_t* z_nary;
90
+ double* z_pt;
91
+ int z_step;
92
+ rfft_plan plan = NULL;
93
+
94
+ if (CLASS_OF(x_val) != numo_cDFloat) {
95
+ x_val = rb_funcall(numo_cDFloat, rb_intern("cast"), 1, x_val);
96
+ }
97
+ if (!RTEST(nary_check_contiguous(x_val))) {
98
+ x_val = nary_dup(x_val);
99
+ }
100
+
101
+ GetNArray(x_val, x_nary);
102
+ n_dims = NA_NDIM(x_nary);
103
+ length = NA_SHAPE(x_nary)[n_dims - 1];
104
+ x_pt = (double*)na_get_pointer_for_read(x_val);
105
+
106
+ plan = make_rfft_plan(length);
107
+ if (!plan) {
108
+ return Qnil;
109
+ }
110
+
111
+ z_shape = ALLOCA_N(size_t, n_dims);
112
+ for (i = 0; i < n_dims - 1; i++) {
113
+ z_shape[i] = NA_SHAPE(x_nary)[i];
114
+ }
115
+ z_shape[n_dims - 1] = length / 2 + 1;
116
+ z_val = rb_narray_new(numo_cDComplex, n_dims, z_shape);
117
+ z_pt = (double*)na_get_pointer_for_write(z_val);
118
+ GetNArray(z_val, z_nary);
119
+ for (i = 0; i < (int)(NA_SIZE(z_nary) * 2); z_pt[i++] = 0.0);
120
+
121
+ z_step = (int)(NA_SHAPE(z_nary)[n_dims - 1]) * 2;
122
+ n_repeats = (int)(NA_SIZE(x_nary)) / length;
123
+ for (i = 0; i < n_repeats; i++) {
124
+ z_pt[z_step - 1] = 0.0;
125
+ memcpy(z_pt + 1, x_pt, length * sizeof(double));
126
+ if (rfft_forward(plan, z_pt + 1, 1.0) != 0) { break; }
127
+ z_pt[0] = z_pt[1];
128
+ z_pt[1] = 0.0;
129
+ z_pt += z_step;
130
+ x_pt += length;
131
+ }
132
+
133
+ if (plan) {
134
+ destroy_rfft_plan(plan);
135
+ }
136
+
137
+ return z_val;
138
+ }
139
+
140
+ /**
141
+ * @!visibility private
142
+ */
143
+ static VALUE numo_pocketfft_irfft(VALUE self, VALUE x_val)
144
+ {
145
+ narray_t* x_nary;
146
+ double* x_pt;
147
+ size_t length;
148
+ int n_dims;
149
+ int n_repeats;
150
+ int i;
151
+ double fct;
152
+ size_t* z_shape;
153
+ VALUE z_val;
154
+ double* z_pt;
155
+ narray_t* z_nary;
156
+ rfft_plan plan = NULL;
157
+
158
+ if (CLASS_OF(x_val) != numo_cDComplex) {
159
+ x_val = rb_funcall(numo_cDComplex, rb_intern("cast"), 1, x_val);
160
+ }
161
+ if (!RTEST(nary_check_contiguous(x_val))) {
162
+ x_val = nary_dup(x_val);
163
+ }
164
+
165
+ GetNArray(x_val, x_nary);
166
+ n_dims = NA_NDIM(x_nary);
167
+ length = NA_SHAPE(x_nary)[n_dims - 1];
168
+ x_pt = (double*)na_get_pointer_for_read(x_val);
169
+ fct = 1.0 / length;
170
+
171
+ plan = make_rfft_plan(length);
172
+ if (!plan) {
173
+ return Qnil;
174
+ }
175
+
176
+ z_shape = ALLOCA_N(size_t, n_dims);
177
+ for (i = 0; i < n_dims - 1; i++) {
178
+ z_shape[i] = NA_SHAPE(x_nary)[i];
179
+ }
180
+ z_shape[n_dims - 1] = length;
181
+ z_val = rb_narray_new(numo_cDFloat, n_dims, z_shape);
182
+ z_pt = (double*)na_get_pointer_for_write(z_val);
183
+ GetNArray(z_val, z_nary);
184
+ for (i = 0; i < (int)NA_SIZE(z_nary); z_pt[i++] = 0.0);
185
+
186
+ n_repeats = (int)(NA_SIZE(z_nary)) / length;
187
+ for (i = 0; i < n_repeats; i++) {
188
+ memcpy(z_pt + 1, x_pt + 2, (length - 1) * sizeof(double));
189
+ z_pt[0] = x_pt[0];
190
+ if (rfft_backward(plan, z_pt, fct) != 0) { break; }
191
+ z_pt += length;
192
+ x_pt += length * 2;
193
+ }
194
+
195
+ if (plan) {
196
+ destroy_rfft_plan(plan);
197
+ }
198
+
199
+ return z_val;
200
+ }
201
+
202
+ void Init_pocketfftext()
203
+ {
204
+ rb_require("numo/narray");
205
+
206
+ mNumo = rb_define_module("Numo");
207
+
208
+ mPocketfft = rb_define_module_under(mNumo, "Pocketfft");
209
+
210
+ rb_define_module_function(mPocketfft, "ext_rfft", numo_pocketfft_rfft, 1);
211
+ rb_define_module_function(mPocketfft, "ext_irfft", numo_pocketfft_irfft, 1);
212
+ rb_define_module_function(mPocketfft, "ext_cfft", numo_pocketfft_cfft, 1);
213
+ rb_define_module_function(mPocketfft, "ext_icfft", numo_pocketfft_icfft, 1);
214
+ }
@@ -0,0 +1,13 @@
1
+ #ifndef NUMO_POCKETFFT_H
2
+ #define NUMO_POCKETFFT_H 1
3
+
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+
7
+ #include <ruby.h>
8
+ #include <numo/narray.h>
9
+ #include <numo/template.h>
10
+
11
+ #include "pocketfft/pocketfft.h"
12
+
13
+ #endif /* NUMO_POCKETFFT_H */
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'numo/narray'
4
+ require 'numo/pocketfft/version'
5
+ require 'numo/pocketfft/pocketfftext'
6
+
7
+ module Numo
8
+ module Pocketfft
9
+ module_function
10
+
11
+ # Compute the 1-dimensional discrete Fourier Transform.
12
+ # @param a [Numo::DFloat/Numo::DComplex] Real or complex 1-dimensional input array.
13
+ # @return [Numo::DComplex] Transformed data.
14
+ def fft(a)
15
+ raise ArgumentError, 'Expect input array to be one-dimensional.' unless a.ndim == 1
16
+ raw_fft(a, 0, inverse: false, real: false)
17
+ end
18
+
19
+ # Compute the 1-dimensional inverse discrete Fourier Transform.
20
+ # @param a [Numo::DComplex] Complex 1-dimensional input array.
21
+ # @return [Numo::DComplex] Inversed transformed data.
22
+ def ifft(a)
23
+ raise ArgumentError, 'Expect input array to be one-dimensional.' unless a.ndim == 1
24
+ raw_fft(a, 0, inverse: true, real: false)
25
+ end
26
+
27
+ # Compute the 2-dimensional discrete Fourier Transform.
28
+ # @param a [Numo::DFloat/Numo::DComplex] Real or complex 2-dimensional input array.
29
+ # @return [Numo::DComplex] Transformed data.
30
+ def fft2(a)
31
+ raise ArgumentError, 'Expect input array to be two-dimensional.' unless a.ndim == 2
32
+ fftn(a)
33
+ end
34
+
35
+ # Compute the 2-dimensional inverse discrete Fourier Transform.
36
+ # @param a [Numo::DComplex] Complex 2-dimensional input array.
37
+ # @return [Numo::DComplex] Inversed transformed data.
38
+ def ifft2(a)
39
+ raise ArgumentError, 'Expect input array to be two-dimensional.' unless a.ndim == 2
40
+ ifftn(a)
41
+ end
42
+
43
+ # Compute the N-dimensional discrete Fourier Transform.
44
+ # @param a [Numo::DFloat/Numo::DComplex] Real or complex input array with any-dimension.
45
+ # @return [Numo::DComplex] Transformed data.
46
+ def fftn(a)
47
+ b = a.dup
48
+ (0...b.ndim).to_a.reverse.each { |ax_id| b = raw_fft(b, ax_id, inverse: false, real: false) }
49
+ b
50
+ end
51
+
52
+ # Compute the N-dimensional inverse discrete Fourier Transform.
53
+ # @param a [Numo::DComplex] Complex input array with any-dimension.
54
+ # @return [Numo::DComplex] Inversed transformed data.
55
+ def ifftn(a)
56
+ b = a.dup
57
+ (0...b.ndim).to_a.each { |ax_id| b = raw_fft(b, ax_id, inverse: true, real: false) }
58
+ b
59
+ end
60
+
61
+ # Compute the 1-dimensional discrete Fourier Transform for real input.
62
+ # @param a [Numo::DFloat] Real 1-dimensional input array.
63
+ # @return [Numo::DComplex] Transformed data.
64
+ def rfft(a)
65
+ raise ArgumentError, 'Expect input array to be one-dimensional.' unless a.ndim == 1
66
+ raw_fft(a, 0, inverse: false, real: true)
67
+ end
68
+
69
+ # Compute the inverse of the 1-dimensional discrete Fourier Transform of real input.
70
+ # @param a [Numo::DComplex] Complex 1-dimensional input array.
71
+ # @return [Numo::DFloat] Inverse transformed data.
72
+ def irfft(a)
73
+ raise ArgumentError, 'Expect input array to be one-dimensional.' unless a.ndim == 1
74
+ raw_fft(a, 0, inverse: true, real: true)
75
+ end
76
+
77
+ # Compute the 2-dimensional discrete Fourier Transform for real input.
78
+ # @param a [Numo::DFloat] Real 2-dimensional input array.
79
+ # @return [Numo::DComplex] Transformed data.
80
+ def rfft2(a)
81
+ raise ArgumentError, 'Expect input array to be two-dimensional.' unless a.ndim == 2
82
+ rfftn(a)
83
+ end
84
+
85
+ # Compute the inverse of the 2-dimensional discrete Fourier Transform of real input.
86
+ # @param a [Numo::DComplex] Complex 2-dimensional input array.
87
+ # @return [Numo::DFloat] Inverse transformed data.
88
+ def irfft2(a)
89
+ raise ArgumentError, 'Expect input array to be two-dimensional.' unless a.ndim == 2
90
+ irfftn(a)
91
+ end
92
+
93
+ # Compute the N-dimensional discrete Fourier Transform for real input.
94
+ # @param a [Numo::DFloat] Real input array with any-dimension.
95
+ # @return [Numo::DComplex] Transformed data.
96
+ def rfftn(a)
97
+ last_axis_id = a.ndim - 1
98
+ b = raw_fft(a, last_axis_id, inverse: false, real: true)
99
+ (0...last_axis_id).to_a.reverse.each { |ax_id| b = raw_fft(b, ax_id, inverse: false, real: false) }
100
+ b
101
+ end
102
+
103
+ # Compute the inverse of the N-dimensional discrete Fourier Transform of real input.
104
+ # @param a [Numo::DComplex] Complex input array with any-dimension.
105
+ # @return [Numo::DFloat] Inverse transformed data.
106
+ def irfftn(a)
107
+ last_axis_id = a.ndim - 1
108
+ b = a.dup
109
+ (0...last_axis_id).to_a.each { |ax_id| b = raw_fft(b, ax_id, inverse: true, real: false) }
110
+ raw_fft(b, last_axis_id, inverse: true, real: true)
111
+ end
112
+
113
+ # @!visibility private
114
+ def raw_fft(a, axis_id, inverse:, real:)
115
+ if axis_id == a.ndim - 1
116
+ if real
117
+ if inverse
118
+ # zero padding
119
+ n = (a.shape[-1] - 1) * 2
120
+ b_shape = a.shape
121
+ b_shape[-1] = n
122
+ b = Numo::DComplex.zeros(*b_shape)
123
+ b_range = [true] * b.ndim
124
+ b_range[-1] = 0...a.shape[-1]
125
+ b[*b_range] = a
126
+ # inverse of dft for real data
127
+ ext_irfft(b)
128
+ else
129
+ ext_rfft(a)
130
+ end
131
+ else
132
+ if inverse
133
+ ext_icfft(a)
134
+ else
135
+ ext_cfft(a)
136
+ end
137
+ end
138
+ else
139
+ if inverse
140
+ ext_icfft(a.swapaxes(axis_id, -1)).swapaxes(axis_id, -1).dup
141
+ else
142
+ ext_cfft(a.swapaxes(axis_id, -1)).swapaxes(axis_id, -1).dup
143
+ end
144
+ end
145
+ end
146
+
147
+ private_class_method :ext_cfft, :ext_icfft, :ext_rfft, :ext_irfft, :raw_fft
148
+ end
149
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Numo is the top level namespace of NUmerical MOdules for Ruby.
4
+ module Numo
5
+ # Numo::Pocketfft is the module that has functions for Fourier transform.
6
+ module Pocketfft
7
+ # The version of Numo::Pocketfft you are using.
8
+ VERSION = '0.1.0'
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'numo/pocketfft/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'numo-pocketfft'
7
+ spec.version = Numo::Pocketfft::VERSION
8
+ spec.authors = ['yoshoku']
9
+ spec.email = ['yoshoku@outlook.com']
10
+
11
+ spec.summary = <<~MSG
12
+ Numo::Pocketfft provides functions for descrete Fourier Transform based on pocketfft.
13
+ MSG
14
+ spec.description = <<~MSG
15
+ Numo::Pocketfft provides functions for descrete Fourier Transform based on pocketfft.
16
+ MSG
17
+ spec.homepage = 'https://github.com/yoshoku/numo-pocketfft'
18
+ spec.license = 'BSD-3-Clause'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+
26
+ # Add files in submodule: https://gist.github.com/mattconnolly/5875987
27
+ gem_dir = __dir__ + '/'
28
+ `git submodule --quiet foreach pwd`.split($OUTPUT_RECORD_SEPARATOR).each do |submodule_path|
29
+ Dir.chdir(submodule_path) do
30
+ submodule_relative_path = submodule_path.sub gem_dir, ''
31
+ `git ls-files`.split($OUTPUT_RECORD_SEPARATOR).each do |filename|
32
+ spec.files << "#{submodule_relative_path}/#{filename}"
33
+ end
34
+ end
35
+ end
36
+
37
+ spec.require_paths = ['lib']
38
+ spec.extensions = ['ext/numo/pocketfft/extconf.rb']
39
+
40
+ spec.add_runtime_dependency 'numo-narray', '~> 0.9.1'
41
+
42
+ spec.add_development_dependency 'bundler', '~> 2.0'
43
+ spec.add_development_dependency 'coveralls', '~> 0.8'
44
+ spec.add_development_dependency 'rake', '~> 10.0'
45
+ spec.add_development_dependency 'rake-compiler', '~> 1.0'
46
+ spec.add_development_dependency 'rspec', '~> 3.0'
47
+ end