fftw.scm 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. ; -*- mode: scheme; coding: utf-8 -*-
  2. ; (c) Daniel Llorens - 2014-2024
  3. ; This library is free software; you can redistribute it and/or modify it under
  4. ; the terms of the GNU Lesser General Public License as published by the Free
  5. ; Software Foundation; either version 3 of the License, or (at your option) any
  6. ; later version.
  7. ;;; Commentary:
  8. ;; Access FFTW through Guile's FFI.
  9. ;;; Code:
  10. (define-module (ffi fftw)
  11. #:export (libfftw3 libfftw3f fftw-dft fftw-dft! FFTW-FORWARD FFTW-BACKWARD))
  12. (import (system foreign) (srfi srfi-1) (srfi srfi-71) (ice-9 match))
  13. ; @TODO As an alternative go through installation.
  14. (define (dylink name)
  15. (catch 'misc-error
  16. (lambda ()
  17. (dynamic-link (let ((lpath (getenv "GUILE_FFI_FFTW_LIBFFTW3_PATH")))
  18. (if (and lpath (not (string=? lpath "")))
  19. (string-append lpath file-name-separator-string name)
  20. name))))
  21. (lambda (k . args)
  22. (format (current-error-port) "Can't find ~a [~a ~a]\n" name k args)
  23. #f)))
  24. (define libfftw3 (dylink "libfftw3"))
  25. (define libfftw3f (dylink "libfftw3f"))
  26. (unless (or libfftw3 libfftw3f)
  27. (throw 'fftw-libraries-cannot-be-found))
  28. ;; http://www.fftw.org/doc/Guru-Complex-DFTs.html#Guru-Complex-DFTs
  29. ;; fftw_plan fftw_plan_guru_dft(int rank, const fftw_iodim *dims,
  30. ;; int howmany_rank, const fftw_iodim *howmany_dims,
  31. ;; fftw_complex *in, fftw_complex *out,
  32. ;; int sign, unsigned flags);
  33. (define-values (fftw_plan_guru_dft fftw_execute fftw_destroy_plan)
  34. (if libfftw3
  35. (values
  36. (pointer->procedure '* (dynamic-func "fftw_plan_guru_dft" libfftw3)
  37. (list int '* int '* '* '* int unsigned-int))
  38. (pointer->procedure void (dynamic-func "fftw_execute" libfftw3)
  39. (list '*))
  40. (pointer->procedure void (dynamic-func "fftw_destroy_plan" libfftw3)
  41. (list '*)))
  42. (values #f #f #f)))
  43. (define-values (fftwf_plan_guru_dft fftwf_execute fftwf_destroy_plan)
  44. (if libfftw3f
  45. (values
  46. (pointer->procedure '* (dynamic-func "fftwf_plan_guru_dft" libfftw3f)
  47. (list int '* int '* '* '* int unsigned-int))
  48. (pointer->procedure void (dynamic-func "fftwf_execute" libfftw3f)
  49. (list '*))
  50. (pointer->procedure void (dynamic-func "fftwf_destroy_plan" libfftw3f)
  51. (list '*)))
  52. (values #f #f #f)))
  53. (define FFTW-ESTIMATE (ash 1 6))
  54. (define FFTW-FORWARD -1)
  55. (define FFTW-BACKWARD +1)
  56. ;; http://www.fftw.org/doc/Guru-vector-and-transform-sizes.html#Guru-vector-and-transform-sizes
  57. (define make-iodims
  58. (let ((lim (ash 1 (+ -1 (* 8 (sizeof int))))))
  59. (lambda (in out)
  60. (append-map (lambda (s-in i-in s-out i-out)
  61. (unless (= lim (max lim s-in i-in s-out i-out)) (throw 'fftw-sizes-too-large))
  62. (unless (= s-in s-out) (throw 'fftw-mismatched-dimensions))
  63. (list s-in i-in i-out))
  64. (array-dimensions in)
  65. (shared-array-increments in)
  66. (array-dimensions out)
  67. (shared-array-increments out)))))
  68. (define (pick-iodims iodims n pick)
  69. (if (zero? n)
  70. (make-c-struct (list int) (list 0))
  71. (make-c-struct (make-list (* 3 n) int) (pick iodims (* 3 n)))))
  72. (define (fftw-dft! k sign in out)
  73. "Compute @var{k}-dimensional DFTs of array @var{in} with given @var{sign}, and
  74. write them to array @var{out}. @var{in} and @var{out} must be arrays of the
  75. same shape and type, either 'c32 or 'c64. For each @var{k}-cell of @var{in}, a
  76. separate @var{k}-DFT is computed and written to the matching cell of @var{out}.
  77. @var{sign} is the sign of the exponent of the transform and can be
  78. @code{FFTW-FORWARD} (-1) or @code{FFTW-BACKWARD} (+1). For example, if @var{in}
  79. has shape @code{(2 2 10 10)}, then after
  80. @lisp
  81. (fftw-dft! 2 FFTW-FORWARD IN OUT)
  82. @end lisp
  83. we have
  84. @example
  85. out[0, 0, ...] = 2D-DFT(in[0, 0, ...])
  86. out[0, 1, ...] = 2D-DFT(in[0, 1, ...])
  87. out[1, 0, ...] = 2D-DFT(in[1, 0, ...])
  88. out[1, 1, ...] = 2D-DFT(in[1, 1, ...])
  89. @end example
  90. If @var{out} is the same array as @var{in}, then @var{out} will contain the
  91. 2D-DFTs of the original contents of @var{in}. If @var{out} aliases @var{in}
  92. without being the same, the contents of both @var{out} and @var{in} become
  93. undefined.
  94. This function returns the output array @var{out}.
  95. See http://www.fftw.org/doc/FFTW-Reference.html for more information.
  96. See also: fttw-dft
  97. "
  98. (define type (array-type in))
  99. (define rank (array-rank in))
  100. (unless (and (eq? type (array-type out)) (or (eq? type 'c64) (eq? type 'c32)))
  101. (throw 'fftw-bad-types type (array-type out)))
  102. (unless (<= 0 k rank)
  103. (throw 'fftw-bad-rank k rank))
  104. (unless (= rank (array-rank out))
  105. (throw 'fftw-mismatched-ranks rank (array-rank out)))
  106. (unless (or (= +1 sign) (= -1 sign))
  107. (throw 'fftw-bad-sign sign))
  108. (let* ((iodims (make-iodims in out))
  109. (transform-k k)
  110. (repeat-k (- rank k))
  111. (real plan execute destroy
  112. (match type
  113. ('c32 (unless libfftw3f (throw 'fftw-is-not-available-for-c32))
  114. (values float fftwf_plan_guru_dft fftwf_execute fftwf_destroy_plan))
  115. ('c64 (unless libfftw3 (throw 'fftw-is-not-available-for-c64))
  116. (values double fftw_plan_guru_dft fftw_execute fftw_destroy_plan))))
  117. (p (plan
  118. transform-k (pick-iodims iodims transform-k take-right)
  119. repeat-k (pick-iodims iodims repeat-k take)
  120. (bytevector->pointer (shared-array-root in) (* (shared-array-offset in) (sizeof real) 2))
  121. (bytevector->pointer (shared-array-root out) (* (shared-array-offset out) (sizeof real) 2))
  122. sign FFTW-ESTIMATE)))
  123. (execute p)
  124. (destroy p)
  125. out))
  126. (define (fftw-dft k sign in)
  127. "Compute @var{k}-dimensional DFTs of 'c64 or 'c32 array @var{in}, with given
  128. @var{sign}, and return the result in a new array of the same shape and type as
  129. @var{in}.
  130. See also: fttw-dft!
  131. "
  132. (let ((out (apply make-typed-array (array-type in) *unspecified* (array-dimensions in))))
  133. (fftw-dft! k sign in out)))