Ruby FFI bindings for FFTW3 (Fastest Fourier Transform in the West).
The gem exposes the FFTW planning and execution APIs for complex DFT, real-to-complex and complex-to-real transforms, real-to-real transforms, the advanced "many" interface, guru and guru64 planners, wisdom import/export, thread control, and runtime metadata.
- Ruby 3.1+
- FFTW 3.3.x shared libraries installed on the system
Ubuntu / Debian:
sudo apt-get install libfftw3-devmacOS (Homebrew):
brew install fftwAdd the gem to your Gemfile:
gem "fftw3-ruby"Then install dependencies:
bundle installRuntime precision support depends on which FFTW libraries are installed.
require "fftw3"
FFTW3.available_precisions
FFTW3.double_available?
FFTW3.float_available?
FFTW3.long_double_available?
FFTW3.quad_available?Notes:
- Plan creation is available for any precision whose FFTW library can be loaded.
FFTW3::AlignedMemory#readand#writecurrently support:double,:float, and:long_double.:quadlibrary availability can be detected withFFTW3.quad_available?, but high-level buffer read/write helpers are not currently provided for quad buffers.
Aligned buffers allocated with FFTW's allocator.
require "fftw3"
input = FFTW3::AlignedMemory.new(8, type: :complex, precision: :double)
input.write(Array.new(8) { |i| Complex(i, -i) })
input.pointer
input.read
input.free!Options:
type:accepts:complexor:realprecision:accepts:double,:float,:long_double, or:quad
Factory methods return FFTW3::Plan instances. A plan can be executed in-place with the buffers it was created with, or with compatible alternate buffers via the new-array execute helpers.
Wrapper around FFTW thread initialization, planner thread count, optional planner locking, and the runtime thread callback hook.
Import and export FFTW wisdom through strings, files, and IO callbacks.
Small wrappers for FFTW global utility functions such as cleanup, set_timelimit, and alignment_of.
require "fftw3"
n = 8
input = FFTW3::AlignedMemory.new(n, type: :complex, precision: :double)
output = FFTW3::AlignedMemory.new(n, type: :complex, precision: :double)
data = (0...n).map { |i| Complex(Math.sin(2 * Math::PI * i / n), 0.0) }
input.write(data)
plan = FFTW3::Plan.dft_1d(
n,
input,
output,
direction: :forward,
flags: [:estimate]
)
plan.execute
result = output.read
puts result.map { |c| "(%.4f, %.4f)" % [c.real, c.imaginary] }
plan.destroy!
input.free!
output.free!require "fftw3"
n = 16
input = FFTW3::AlignedMemory.new(n, type: :real)
output = FFTW3::AlignedMemory.new((n / 2) + 1, type: :complex)
input.write(n.times.map { |i| Math.sin(2 * Math::PI * i / n) })
plan = FFTW3::Plan.dft_r2c_1d(n, input, output)
plan.execute
result = output.readrequire "fftw3"
n = 8
input_a = FFTW3::AlignedMemory.new(n, type: :complex)
output_a = FFTW3::AlignedMemory.new(n, type: :complex)
input_b = FFTW3::AlignedMemory.new(n, type: :complex)
output_b = FFTW3::AlignedMemory.new(n, type: :complex)
input_a.write(Array.new(n) { |i| Complex(i, 0.0) })
input_b.write(Array.new(n) { |i| Complex(0.0, i) })
plan = FFTW3::Plan.dft_1d(n, input_a, output_a, direction: :forward)
# Reuse the same plan with different buffers of the same shape.
plan.execute_dft(input_b, output_b)
plan.destroy!
input_a.free!
output_a.free!
input_b.free!
output_b.free!require "fftw3"
n = 1024
input = FFTW3::AlignedMemory.new(n, type: :complex)
output = FFTW3::AlignedMemory.new(n, type: :complex)
FFTW3::Threads.init
FFTW3::Threads.plan_with_nthreads(4)
plan = FFTW3::Plan.dft_1d(n, input, output, direction: :forward, flags: [:measure])
plan.execute
FFTW3::Threads.planner_nthreads
plan.destroy!
input.free!
output.free!
FFTW3::Threads.cleanuprequire "fftw3"
FFTW3::Threads.init
FFTW3::Threads.make_planner_thread_safe
FFTW3::Threads.cleanupmake_planner_thread_safe returns nil when the linked FFTW build does not expose that function.
threads_set_callback is optional in FFTW. Check support before using it, and set it before creating plans.
require "fftw3"
if FFTW3::Threads.set_callback_supported?
marker = FFI::MemoryPointer.new(:int)
marker.write_int(7)
FFTW3::Threads.set_callback(data: marker) do |work, jobdata, elsize, njobs, data_ptr|
njobs.times do |index|
work.call(jobdata + (elsize * index))
end
end
# Reset to the default FFTW backend.
FFTW3::Threads.set_callback(nil)
endrequire "fftw3"
wisdom = FFTW3::Wisdom.export_to_string
FFTW3::Wisdom.import_from_string(wisdom)
FFTW3::Wisdom.export_to_file("wisdom.fftw")
FFTW3::Wisdom.import_from_file("wisdom.fftw")require "fftw3"
require "stringio"
io = StringIO.new
FFTW3::Wisdom.export_to_io(io)
FFTW3::Wisdom.import_from_io(StringIO.new(io.string))require "fftw3"
FFTW3::Wisdom.import_system
FFTW3::Wisdom.forget!require "fftw3"
input = FFTW3::AlignedMemory.new(8, type: :complex)
FFTW3.version
FFTW3.compiler
FFTW3.codelet_optimizations
FFTW3::Utils.set_timelimit(0.25)
FFTW3::Utils.alignment_of(input.pointer)
FFTW3::Utils.cleanup
input.free!| Method | Description |
|---|---|
available_precisions |
Returns the precisions whose FFTW libraries can be loaded |
double_available? / float_available? / long_double_available? / quad_available? |
Per-precision availability checks |
version |
Linked FFTW version string |
compiler |
Linked FFTW compiler string |
codelet_optimizations |
Linked FFTW codelet optimization string |
| Method group | Methods |
|---|---|
| Complex DFT | dft_1d, dft_2d, dft_3d, dft |
| Real-to-complex | dft_r2c_1d, dft_r2c_2d, dft_r2c_3d, dft_r2c |
| Complex-to-real | dft_c2r_1d, dft_c2r_2d, dft_c2r_3d, dft_c2r |
| Real-to-real | r2r_1d, r2r_2d, r2r_3d, r2r |
| Advanced / batched | many_dft, many_dft_r2c, many_dft_c2r, many_r2r |
| Guru | guru_dft, guru_split_dft, guru_dft_r2c, guru_split_dft_r2c, guru_dft_c2r, guru_split_dft_c2r, guru_r2r |
| Guru64 | guru64_dft, guru64_split_dft, guru64_dft_r2c, guru64_split_dft_r2c, guru64_dft_c2r, guru64_split_dft_c2r, guru64_r2r |
Notes:
dft_*andmany_dftusedirection:.guru_dftandguru64_dftusesign:.- Guru and guru64 dimension arrays use hashes shaped like
{ n:, is:, os: }.
| Method | Description |
|---|---|
execute |
Execute with the buffers used to create the plan |
execute_dft |
New-array execute for complex DFT |
execute_dft_r2c |
New-array execute for real-to-complex DFT |
execute_dft_c2r |
New-array execute for complex-to-real DFT |
execute_r2r |
New-array execute for real-to-real transforms |
execute_split_dft |
New-array execute for split-complex guru DFT |
execute_split_dft_r2c |
New-array execute for split real-to-complex guru DFT |
execute_split_dft_c2r |
New-array execute for split complex-to-real guru DFT |
flops |
Returns { add:, mul:, fma: } |
cost |
FFTW plan cost |
estimate_cost |
FFTW estimated plan cost |
to_s / description |
String representation of the plan |
print(io = $stdout) |
Write the plan description to an IO object |
destroy! |
Destroy the underlying FFTW plan |
| Method | Description |
|---|---|
init |
Initialize FFTW thread support |
plan_with_nthreads(n) |
Set planner and execution thread count |
cleanup |
Release FFTW thread state |
planner_nthreads |
Read configured thread count when supported |
make_planner_thread_safe |
Enable FFTW's optional planner lock when supported |
set_callback_supported? |
Check whether threads_set_callback is available |
set_callback(callback = nil, data: nil, &block) |
Install or clear FFTW's runtime threading callback |
| Method | Description |
|---|---|
export_to_string / import_from_string |
Wisdom through Ruby strings |
export_to_file / import_from_file |
Wisdom through filesystem paths |
export_to_io / import_from_io |
Wisdom through Ruby IO callbacks |
import_system |
Best-effort import from the system wisdom store |
forget! |
Clear loaded wisdom |
| Method | Description |
|---|---|
cleanup |
Call FFTW global cleanup |
set_timelimit(seconds) |
Set the FFTW planner time limit |
alignment_of(pointer) |
Query FFTW's alignment classification for a pointer |
Planner flags:
FFTW3::ESTIMATEFFTW3::MEASUREFFTW3::PATIENTFFTW3::EXHAUSTIVEFFTW3::PRESERVE_INPUTFFTW3::DESTROY_INPUTFFTW3::WISDOM_ONLY- Additional exported flags such as
FFTW3::ESTIMATE_PATIENT,FFTW3::ALLOW_PRUNING, andFFTW3::NO_SIMD
Direction constants:
FFTW3::FORWARDFFTW3::BACKWARD:forwardand:backwardare also accepted where direction symbols are supported
R2R kinds:
FFTW3::R2HCFFTW3::HC2RFFTW3::DHTFFTW3::REDFT00,REDFT01,REDFT10,REDFT11FFTW3::RODFT00,RODFT01,RODFT10,RODFT11
bundle install
bundle exec rake specThe gem is available as open source under the terms of the MIT License.
FFTW3 itself is licensed under GPLv2+. Using this gem requires an installed FFTW3 library.