FFTW3-Ruby
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.
Requirements
- Ruby 3.1+
- FFTW 3.3.x shared libraries installed on the system
Installing FFTW3
Ubuntu / Debian:
sudo apt-get install libfftw3-dev
macOS (Homebrew):
brew install fftw
Installation
Add the gem to your Gemfile:
gem "fftw3-ruby"
Then install dependencies:
bundle install
Precision Support
Runtime 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.
Core Types
FFTW3::AlignedMemory
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
FFTW3::Plan
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.
FFTW3::Threads
Wrapper around FFTW thread initialization, planner thread count, optional planner locking, and the runtime thread callback hook.
FFTW3::Wisdom
Import and export FFTW wisdom through strings, files, and IO callbacks.
FFTW3::Utils
Small wrappers for FFTW global utility functions such as cleanup, set_timelimit, and alignment_of.
Quick Start
Basic 1D Complex DFT
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!
Real-to-Complex
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.read
New-array Execute
require "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!
Threading
Planner Thread Count
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.cleanup
Planner Locking
require "fftw3"
FFTW3::Threads.init
FFTW3::Threads.make_planner_thread_safe
FFTW3::Threads.cleanup
make_planner_thread_safe returns nil when the linked FFTW build does not expose that function.
Custom Thread Backend Callback
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)
end
Wisdom
String and File APIs
require "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")
IO Callback APIs
require "fftw3"
require "stringio"
io = StringIO.new
FFTW3::Wisdom.export_to_io(io)
FFTW3::Wisdom.import_from_io(StringIO.new(io.string))
System Wisdom
require "fftw3"
FFTW3::Wisdom.import_system
FFTW3::Wisdom.forget!
Runtime Metadata and Utilities
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!
API Reference
Top-level FFTW3
| 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 |
FFTW3::Plan Factory Methods
| 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: }.
FFTW3::Plan Instance Methods
| 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 |
FFTW3::Threads
| 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 |
FFTW3::Wisdom
| 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 |
FFTW3::Utils
| 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 |
Constants
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
Development
bundle install
bundle exec rake spec
License
The 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.