-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathfft.lisp
155 lines (144 loc) · 5.87 KB
/
fft.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
(defpackage :cp/fft
(:use :cl)
(:export #:fft-float #:dft! #:inverse-dft! #:with-fixed-length-fft #:convolve!)
(:documentation "Provides complex FFT.
Reference:
http://www.prefield.com/algorithm/math/fft.html
http://techtipshoge.blogspot.com/2011/08/fft4.html (Japanese)"))
(in-package :cp/fft)
(deftype fft-float () 'double-float)
(declaim (inline power2-p))
(defun power2-p (x)
"Checks if X is a power of 2."
(zerop (logand x (- x 1))))
(defun %dft! (f sign)
(declare (optimize (speed 3) (safety 0))
((simple-array (complex fft-float) (*)) f)
((integer -1 1) sign))
(prog1 f
(let* ((n (length f))
(theta (* sign (/ (coerce (* 2 pi) 'fft-float) n))))
(declare (fft-float theta))
(assert (power2-p n))
(do ((m n (ash m -1)))
((= m 1))
(declare ((integer 0 #.most-positive-fixnum) m))
(let ((mh (ash m -1)))
(declare ((integer 0 #.most-positive-fixnum) mh))
(dotimes (i mh)
(let ((w (cis (* i theta))))
(do ((j i (+ j m)))
((>= j n))
(declare ((integer 0 #.most-positive-fixnum) j))
(let* ((k (+ j mh))
(xt (- (aref f j) (aref f k))))
(declare ((integer 0 #.most-positive-fixnum) k))
(incf (aref f j) (aref f k))
(setf (aref f k) (* w xt))))))
(setq theta (* theta 2))))
;; bit-reverse ordering
(let ((i 0))
(declare ((integer 0 #.most-positive-fixnum) i))
(loop for j from 1 below (- n 1)
do (loop for k of-type (integer 0 #.most-positive-fixnum)
= (ash n -1) then (ash k -1)
while (> k (setq i (logxor i k))))
(when (< j i)
(rotatef (aref f i) (aref f j))))))))
;; For FFT of fixed length, preparing the table of exp(i*theta) will be
;; efficient.
(defun %make-exp-table (n sign)
(declare (optimize (speed 3))
((integer 0 #.most-positive-fixnum) n)
((integer -1 1) sign))
(assert (power2-p n))
(let* ((table (make-array (ash n -1) :element-type '(complex fft-float)))
(theta (* sign (/ (coerce (* 2 pi) 'fft-float) n))))
(dotimes (i (length table))
(setf (aref table i) (cis (* i theta))))
table))
(defparameter *exp-table+* nil)
(defparameter *exp-table-* nil)
(defmacro with-fixed-length-fft (size &body body)
"Makes FFT faster when the size of target vectors is fixed in BODY. This macro
computes and holds the roots of unity for SIZE, which DFT! and INVERSE-DFT!
called in BODY automatically uses; they will signal an error when they receive a
vector of different size."
(let ((s (gensym)))
`(let ((,s ,size))
(let ((*exp-table+* (%make-exp-table ,s 1))
(*exp-table-* (%make-exp-table ,s -1)))
,@body))))
(defun %dft-cached-cis! (f sign)
(declare (optimize (speed 3) (safety 0))
((simple-array (complex fft-float) (*)) f)
((integer -1 1) sign))
(prog1 f
(let ((n (length f))
(table (if (= 1 sign) *exp-table+* *exp-table-*)))
(declare ((simple-array (complex fft-float) (*)) table))
(assert (power2-p n))
(assert (>= (length table) (ash n -1)))
(do ((m n (ash m -1))
(shift 0 (+ shift 1)))
((= m 1))
(declare ((integer 0 #.most-positive-fixnum) m shift))
(let ((mh (ash m -1)))
(dotimes (i mh)
(do ((j i (+ j m)))
((>= j n))
(declare ((integer 0 #.most-positive-fixnum) j))
(let* ((k (+ j mh))
(xt (- (aref f j) (aref f k)))
(cis-index (ash i shift)))
(declare ((integer 0 #.most-positive-fixnum) k cis-index))
(incf (aref f j) (aref f k))
(setf (aref f k) (* (aref table cis-index) xt)))))))
;; bit-reverse ordering
(let ((i 0))
(declare ((integer 0 #.most-positive-fixnum) i))
(loop for j from 1 below (- n 1)
do (loop for k of-type (integer 0 #.most-positive-fixnum)
= (ash n -1) then (ash k -1)
while (> k (setq i (logxor i k))))
(when (< j i)
(rotatef (aref f i) (aref f j))))))))
(declaim (inline dft!))
(defun dft! (vector)
"Does DFT on VECTOR. The length of VECTOR must be a power of 2."
(declare ((simple-array (complex fft-float) (*)) vector))
(if (zerop (length vector))
vector
(if *exp-table+*
(%dft-cached-cis! vector 1)
(%dft! vector 1))))
(declaim (inline inverse-dft!))
(defun inverse-dft! (vector)
"Does inverse DFT on VECTOR. The length of VECTOR must be a power of 2."
(declare ((simple-array (complex fft-float) (*)) vector))
(prog1 vector
(let ((n (length vector)))
(unless (zerop n)
(let ((/n (/ (coerce n 'fft-float))))
(if *exp-table-*
(%dft-cached-cis! vector -1)
(%dft! vector -1))
(dotimes (i n)
(setf (aref vector i) (* (aref vector i) /n))))))))
(declaim (inline convolve!))
(defun convolve! (vector1 vector2 &optional result-vector)
"Returns the convolution of two vectors VECTOR1 and VECTOR2. A new vector is
created when RESULT-VECTOR is null. This function destructively modifies VECTOR1
and VECTOR2. (They can be restored by INVERSE-DFT!.)"
(declare ((simple-array (complex fft-float) (*)) vector1 vector2)
((or null (simple-array (complex fft-float) (*))) result-vector))
(let ((n (length vector1)))
(assert (and (power2-p n)
(= n (length vector2))))
(dft! vector1)
(dft! vector2)
(let ((result (or result-vector
(make-array n :element-type '(complex fft-float)))))
(dotimes (i n)
(setf (aref result i) (* (aref vector1 i) (aref vector2 i))))
(inverse-dft! result))))