Permalink
Browse files

QuickCheck saves the day once again!

The largest vectors I'd tested on by hand was 8, but when I randomised
the vector size, QuickCheck immediately highlighted an infinite loop
at sizes above 8.

Now I even have a good bound on the error that creeps in due to the FFT
implementation!
  • Loading branch information...
1 parent 2bc4822 commit 9d4536605ac7cb808983d14c3a1f94c5a5b38025 @bos committed Oct 20, 2011
Showing with 20 additions and 12 deletions.
  1. +3 −3 Statistics/Transform.hs
  2. +17 −9 tests/Tests/Transform.hs
View
@@ -73,10 +73,10 @@ mfft vec
| 1 `shiftL` m /= len = error "Statistics.Transform.fft: bad vector size"
| otherwise = bitReverse 0 0
where
- bitReverse i j | i == (len-1) = stage 0 1
- | otherwise = do
+ bitReverse i j | i == len-1 = stage 0 1
+ | otherwise = do
when (i < j) $ M.swap vec i j
- let inner k l | k <= l = inner (l-k) (k `shiftR` 1)
+ let inner k l | k <= l = inner (k `shiftR` 1) (l-k)
| otherwise = bitReverse (i+1) (l+k)
inner (len `shiftR` 1) j
stage l !l1 | l == m = return ()
View
@@ -3,6 +3,8 @@ module Tests.Transform
tests
) where
+import Debug.Trace
+import Data.Bits ((.&.), shiftL)
import Data.Complex (Complex((:+)))
import Statistics.Function (within)
import Statistics.Transform (CD, fft)
@@ -20,22 +22,28 @@ tests = testGroup "fft" [
-- A single real-valued impulse at the beginning of an otherwise zero
-- vector should be replicated in every real component of the result,
-- and all the imaginary components should be zero.
-t_impulse :: Double -> Bool
-t_impulse k = G.all (c_within 2 i) (fft v)
+t_impulse :: Double -> Positive Int -> Bool
+t_impulse k (Positive m) = G.all (c_near i) (fft v)
where v = i `G.cons` G.replicate (n-1) 0
i = k :+ 0
- n = 8
+ n = 1 `shiftL` (m .&. 6)
-- If a real-valued impulse is offset from the beginning of an
-- otherwise zero vector, the sum-of-squares of each component of the
-- result should equal the square of the impulse.
-t_impulse_offset :: Double -> Positive Int -> Bool
-t_impulse_offset k (Positive x) = G.all ok (fft v)
+t_impulse_offset :: Double -> Positive Int -> Positive Int -> Bool
+t_impulse_offset k (Positive x) (Positive m) = G.all ok (fft v) || traceShow (k,v) False
where v = G.concat [G.replicate xn 0, G.singleton i, G.replicate (n-xn-1) 0]
- ok (re :+ im) = within 2 (re*re + im*im) (k*k)
+ ok (re :+ im) = within ulps (re*re + im*im) (k*k)
i = k :+ 0
xn = x `rem` n
- n = 8
+ n = 1 `shiftL` (m .&. 6)
-c_within :: Int -> CD -> CD -> Bool
-c_within ulps (a :+ b) (c :+ d) = within ulps a b && within ulps b d
+-- With an error tolerance of 8 ULPs, a million QuickCheck tests are
+-- likely to all succeed. With a tolerance of 7, we fail around the
+-- half million mark.
+ulps :: Int
+ulps = 8
+
+c_near :: CD -> CD -> Bool
+c_near (a :+ b) (c :+ d) = within ulps a c && within ulps b d

0 comments on commit 9d45366

Please sign in to comment.