numo-pocketfft 0.1.0

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.
@@ -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