fftw3-ruby 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,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FFTW3
4
+ module FFI
5
+ module BindingTemplate
6
+ def self.define_bindings(mod, prefix, lib_names) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
7
+ mod.instance_variable_set(:@available, false)
8
+
9
+ mod.define_singleton_method(:available?) { @available }
10
+
11
+ begin
12
+ mod.module_eval do
13
+ extend ::FFI::Library
14
+ ffi_lib lib_names
15
+
16
+ callback :"#{prefix}write_char_func", [:char, :pointer], :void
17
+ callback :"#{prefix}read_char_func", [:pointer], :int
18
+
19
+ # --- Plan Execution / Destruction ---
20
+ attach_function :"#{prefix}execute", [:pointer], :void
21
+ attach_function :"#{prefix}destroy_plan", [:pointer], :void
22
+
23
+ # --- Basic: Complex DFT ---
24
+ attach_function :"#{prefix}plan_dft",
25
+ [:int, :pointer, :pointer, :pointer, :int, :uint], :pointer
26
+ attach_function :"#{prefix}plan_dft_1d",
27
+ [:int, :pointer, :pointer, :int, :uint], :pointer
28
+ attach_function :"#{prefix}plan_dft_2d",
29
+ [:int, :int, :pointer, :pointer, :int, :uint], :pointer
30
+ attach_function :"#{prefix}plan_dft_3d",
31
+ [:int, :int, :int, :pointer, :pointer, :int, :uint], :pointer
32
+
33
+ # --- Basic: R2C ---
34
+ attach_function :"#{prefix}plan_dft_r2c",
35
+ [:int, :pointer, :pointer, :pointer, :uint], :pointer
36
+ attach_function :"#{prefix}plan_dft_r2c_1d",
37
+ [:int, :pointer, :pointer, :uint], :pointer
38
+ attach_function :"#{prefix}plan_dft_r2c_2d",
39
+ [:int, :int, :pointer, :pointer, :uint], :pointer
40
+ attach_function :"#{prefix}plan_dft_r2c_3d",
41
+ [:int, :int, :int, :pointer, :pointer, :uint], :pointer
42
+
43
+ # --- Basic: C2R ---
44
+ attach_function :"#{prefix}plan_dft_c2r",
45
+ [:int, :pointer, :pointer, :pointer, :uint], :pointer
46
+ attach_function :"#{prefix}plan_dft_c2r_1d",
47
+ [:int, :pointer, :pointer, :uint], :pointer
48
+ attach_function :"#{prefix}plan_dft_c2r_2d",
49
+ [:int, :int, :pointer, :pointer, :uint], :pointer
50
+ attach_function :"#{prefix}plan_dft_c2r_3d",
51
+ [:int, :int, :int, :pointer, :pointer, :uint], :pointer
52
+
53
+ # --- Basic: R2R ---
54
+ attach_function :"#{prefix}plan_r2r",
55
+ [:int, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
56
+ attach_function :"#{prefix}plan_r2r_1d",
57
+ [:int, :pointer, :pointer, :int, :uint], :pointer
58
+ attach_function :"#{prefix}plan_r2r_2d",
59
+ [:int, :int, :pointer, :pointer, :int, :int, :uint], :pointer
60
+ attach_function :"#{prefix}plan_r2r_3d",
61
+ [:int, :int, :int, :pointer, :pointer, :int, :int, :int, :uint], :pointer
62
+
63
+ # --- Advanced (many) ---
64
+ attach_function :"#{prefix}plan_many_dft",
65
+ [:int, :pointer, :int,
66
+ :pointer, :pointer, :int, :int,
67
+ :pointer, :pointer, :int, :int,
68
+ :int, :uint], :pointer
69
+ attach_function :"#{prefix}plan_many_dft_r2c",
70
+ [:int, :pointer, :int,
71
+ :pointer, :pointer, :int, :int,
72
+ :pointer, :pointer, :int, :int,
73
+ :uint], :pointer
74
+ attach_function :"#{prefix}plan_many_dft_c2r",
75
+ [:int, :pointer, :int,
76
+ :pointer, :pointer, :int, :int,
77
+ :pointer, :pointer, :int, :int,
78
+ :uint], :pointer
79
+ attach_function :"#{prefix}plan_many_r2r",
80
+ [:int, :pointer, :int,
81
+ :pointer, :pointer, :int, :int,
82
+ :pointer, :pointer, :int, :int,
83
+ :pointer, :uint], :pointer
84
+
85
+ # --- Guru ---
86
+ attach_function :"#{prefix}plan_guru_dft",
87
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :int, :uint], :pointer
88
+ attach_function :"#{prefix}plan_guru_split_dft",
89
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
90
+ attach_function :"#{prefix}plan_guru_dft_r2c",
91
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :uint], :pointer
92
+ attach_function :"#{prefix}plan_guru_split_dft_r2c",
93
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
94
+ attach_function :"#{prefix}plan_guru_dft_c2r",
95
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :uint], :pointer
96
+ attach_function :"#{prefix}plan_guru_split_dft_c2r",
97
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
98
+ attach_function :"#{prefix}plan_guru_r2r",
99
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
100
+
101
+ # --- Guru64 ---
102
+ attach_function :"#{prefix}plan_guru64_dft",
103
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :int, :uint], :pointer
104
+ attach_function :"#{prefix}plan_guru64_split_dft",
105
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
106
+ attach_function :"#{prefix}plan_guru64_dft_r2c",
107
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :uint], :pointer
108
+ attach_function :"#{prefix}plan_guru64_split_dft_r2c",
109
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
110
+ attach_function :"#{prefix}plan_guru64_dft_c2r",
111
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :uint], :pointer
112
+ attach_function :"#{prefix}plan_guru64_split_dft_c2r",
113
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
114
+ attach_function :"#{prefix}plan_guru64_r2r",
115
+ [:int, :pointer, :int, :pointer, :pointer, :pointer, :pointer, :uint], :pointer
116
+
117
+ # --- New-array Execute ---
118
+ attach_function :"#{prefix}execute_dft", [:pointer, :pointer, :pointer], :void
119
+ attach_function :"#{prefix}execute_split_dft", [:pointer, :pointer, :pointer, :pointer, :pointer], :void
120
+ attach_function :"#{prefix}execute_dft_r2c", [:pointer, :pointer, :pointer], :void
121
+ attach_function :"#{prefix}execute_split_dft_r2c", [:pointer, :pointer, :pointer, :pointer], :void
122
+ attach_function :"#{prefix}execute_dft_c2r", [:pointer, :pointer, :pointer], :void
123
+ attach_function :"#{prefix}execute_split_dft_c2r", [:pointer, :pointer, :pointer, :pointer], :void
124
+ attach_function :"#{prefix}execute_r2r", [:pointer, :pointer, :pointer], :void
125
+
126
+ # --- Memory ---
127
+ attach_function :"#{prefix}malloc", [:size_t], :pointer
128
+ attach_function :"#{prefix}alloc_real", [:size_t], :pointer
129
+ attach_function :"#{prefix}alloc_complex", [:size_t], :pointer
130
+ attach_function :"#{prefix}free", [:pointer], :void
131
+
132
+ # --- Wisdom ---
133
+ attach_function :"#{prefix}export_wisdom_to_filename", [:string], :int
134
+ attach_function :"#{prefix}export_wisdom_to_file", [:pointer], :void
135
+ attach_function :"#{prefix}export_wisdom_to_string", [], :pointer
136
+ attach_function :"#{prefix}export_wisdom", [:"#{prefix}write_char_func", :pointer], :void
137
+ attach_function :"#{prefix}import_system_wisdom", [], :int
138
+ attach_function :"#{prefix}import_wisdom_from_filename", [:string], :int
139
+ attach_function :"#{prefix}import_wisdom_from_file", [:pointer], :int
140
+ attach_function :"#{prefix}import_wisdom_from_string", [:string], :int
141
+ attach_function :"#{prefix}import_wisdom", [:"#{prefix}read_char_func", :pointer], :int
142
+ attach_function :"#{prefix}forget_wisdom", [], :void
143
+
144
+ # --- Utility ---
145
+ attach_function :"#{prefix}cleanup", [], :void
146
+ attach_function :"#{prefix}set_timelimit", [:double], :void
147
+ attach_function :"#{prefix}flops", [:pointer, :pointer, :pointer, :pointer], :void
148
+ attach_function :"#{prefix}estimate_cost", [:pointer], :double
149
+ attach_function :"#{prefix}cost", [:pointer], :double
150
+ attach_function :"#{prefix}alignment_of", [:pointer], :int
151
+ attach_function :"#{prefix}fprint_plan", [:pointer, :pointer], :void
152
+ attach_function :"#{prefix}print_plan", [:pointer], :void
153
+ attach_function :"#{prefix}sprint_plan", [:pointer], :pointer
154
+ end
155
+
156
+ mod.instance_variable_set(:@available, true)
157
+ rescue LoadError
158
+ # Library not available — module remains unavailable
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "binding_template"
4
+
5
+ module FFTW3
6
+ module FFI
7
+ module Double; end
8
+
9
+ BindingTemplate.define_bindings(
10
+ Double,
11
+ "fftw_",
12
+ LibraryLoader.find(:double)
13
+ )
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "binding_template"
4
+
5
+ module FFTW3
6
+ module FFI
7
+ module Float; end
8
+
9
+ BindingTemplate.define_bindings(
10
+ Float,
11
+ "fftwf_",
12
+ LibraryLoader.find(:float)
13
+ )
14
+ end
15
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FFTW3
4
+ module FFI
5
+ module LibraryLoader
6
+ LIBRARY_NAMES = {
7
+ double: %w[libfftw3.so libfftw3.dylib fftw3.dll fftw3],
8
+ float: %w[libfftw3f.so libfftw3f.dylib fftw3f.dll fftw3f],
9
+ long_double: %w[libfftw3l.so libfftw3l.dylib fftw3l.dll fftw3l],
10
+ quad: %w[libfftw3q.so libfftw3q.dylib fftw3q.dll fftw3q],
11
+ threads_double: %w[libfftw3_threads.so libfftw3_threads.dylib fftw3_threads],
12
+ threads_float: %w[libfftw3f_threads.so libfftw3f_threads.dylib fftw3f_threads],
13
+ threads_long_double: %w[libfftw3l_threads.so libfftw3l_threads.dylib fftw3l_threads],
14
+ threads_quad: %w[libfftw3q_threads.so libfftw3q_threads.dylib fftw3q_threads]
15
+ }.freeze
16
+
17
+ def self.find(precision)
18
+ names = LIBRARY_NAMES.fetch(precision)
19
+ lib_dir = ENV["FFTW3_LIB_DIR"]
20
+
21
+ if lib_dir
22
+ names.each do |name|
23
+ path = File.join(lib_dir, name)
24
+ return path if File.exist?(path)
25
+ end
26
+ end
27
+
28
+ names
29
+ end
30
+
31
+ def self.find_threads(precision)
32
+ key = :"threads_#{precision}"
33
+ find(key)
34
+ end
35
+
36
+ def self.open(precision)
37
+ @dynamic_libraries ||= {}
38
+ return @dynamic_libraries[precision] if @dynamic_libraries.key?(precision)
39
+
40
+ loaded_path = begin
41
+ fftw_module = FFTW3::FFI.module_for(precision)
42
+ fftw_module.ffi_libraries.first&.name if fftw_module.respond_to?(:ffi_libraries)
43
+ rescue FFTW3::Error::InvalidArgument
44
+ nil
45
+ end
46
+
47
+ library_names = [loaded_path, *Array(find(precision))].compact.uniq
48
+ library = library_names.filter_map do |name|
49
+ begin
50
+ ::FFI::DynamicLibrary.open(name, ::FFI::DynamicLibrary::RTLD_LAZY)
51
+ rescue LoadError, RuntimeError
52
+ nil
53
+ end
54
+ end.first
55
+
56
+ return @dynamic_libraries[precision] = library if library
57
+
58
+ raise FFTW3::Error::LibraryNotFound, "Unable to load FFTW library for #{precision}"
59
+ end
60
+ end
61
+
62
+ PRECISION_CONFIG = {
63
+ double: { prefix: "fftw_", real_type: :double },
64
+ float: { prefix: "fftwf_", real_type: :float },
65
+ long_double: { prefix: "fftwl_", real_type: :long_double },
66
+ quad: { prefix: "fftwq_", real_type: :float128 }
67
+ }.freeze
68
+
69
+ def self.module_for(precision)
70
+ case precision
71
+ when :double then Double
72
+ when :float then Float
73
+ when :long_double then LongDouble
74
+ when :quad then Quad
75
+ else raise FFTW3::Error::InvalidArgument, "Unknown precision: #{precision}"
76
+ end
77
+ end
78
+
79
+ def self.prefix_for(precision)
80
+ PRECISION_CONFIG.fetch(precision)[:prefix]
81
+ end
82
+
83
+ def self.string_constant(precision, name)
84
+ symbol = "#{prefix_for(precision)}#{name}"
85
+ LibraryLoader.open(precision).find_variable(symbol).read_string
86
+ rescue FFTW3::Error::LibraryNotFound, LoadError, RuntimeError
87
+ nil
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "binding_template"
4
+
5
+ module FFTW3
6
+ module FFI
7
+ module LongDouble; end
8
+
9
+ BindingTemplate.define_bindings(
10
+ LongDouble,
11
+ "fftwl_",
12
+ LibraryLoader.find(:long_double)
13
+ )
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "binding_template"
4
+
5
+ module FFTW3
6
+ module FFI
7
+ module Quad; end
8
+
9
+ BindingTemplate.define_bindings(
10
+ Quad,
11
+ "fftwq_",
12
+ LibraryLoader.find(:quad)
13
+ )
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+
5
+ module FFTW3
6
+ module FFI
7
+ module Types
8
+ class IODimStruct < ::FFI::Struct
9
+ layout :n, :int,
10
+ :is, :int,
11
+ :os, :int
12
+ end
13
+
14
+ class IODim64Struct < ::FFI::Struct
15
+ layout :n, :int64,
16
+ :is, :int64,
17
+ :os, :int64
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FFTW3
4
+ class IODim
5
+ def self.to_ffi(dims)
6
+ ptr = ::FFI::MemoryPointer.new(FFTW3::FFI::Types::IODimStruct, dims.length)
7
+ dims.each_with_index do |d, i|
8
+ struct = FFTW3::FFI::Types::IODimStruct.new(ptr + i * FFTW3::FFI::Types::IODimStruct.size)
9
+ struct[:n] = d[:n]
10
+ struct[:is] = d[:is]
11
+ struct[:os] = d[:os]
12
+ end
13
+ ptr
14
+ end
15
+ end
16
+
17
+ class IODim64
18
+ def self.to_ffi(dims)
19
+ ptr = ::FFI::MemoryPointer.new(FFTW3::FFI::Types::IODim64Struct, dims.length)
20
+ dims.each_with_index do |d, i|
21
+ struct = FFTW3::FFI::Types::IODim64Struct.new(ptr + i * FFTW3::FFI::Types::IODim64Struct.size)
22
+ struct[:n] = d[:n]
23
+ struct[:is] = d[:is]
24
+ struct[:os] = d[:os]
25
+ end
26
+ ptr
27
+ end
28
+ end
29
+ end