summaryrefslogtreecommitdiff
blob: 5dd2d9c622f798f22ae587173d719670f6132297 (plain)
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
From 7e5deed1cb3fafdd6eb035b3713ae2f46b67014a Mon Sep 17 00:00:00 2001
From: Alexey Khudyakov <alexey.skladnoy@gmail.com>
Date: Thu, 8 Jun 2023 13:26:11 +0300
Bug: https://github.com/haskell/math-functions/pull/75
Signed-off-by: hololeap <hololeap@protonmail.com>
Subject: [PATCH] Fix test suite

QC as of 2.14.3. became much better at generating test cases and started
reliably failing Kahan summation

This was fixed by tweaking badvec to be just very bad. Not outrageously
bad.
---
 tests/Tests/Sum.hs | 72 +++++++++++++++++++++++++++-------------------
 1 file changed, 43 insertions(+), 29 deletions(-)

diff --git a/tests/Tests/Sum.hs b/tests/Tests/Sum.hs
index 08eaf1e..1fcb2e9 100644
--- a/tests/Tests/Sum.hs
+++ b/tests/Tests/Sum.hs
@@ -4,54 +4,68 @@ module Tests.Sum (tests) where
 
 import Control.Applicative ((<$>))
 import Numeric.Sum as Sum
+import Numeric.MathFunctions.Comparison
 import Prelude hiding (sum)
 import Test.Tasty (TestTree, testGroup)
-import Test.Tasty.QuickCheck (testProperty)
+import Test.Tasty.QuickCheck
 import Test.QuickCheck (Arbitrary(..))
 import qualified Prelude
 
-t_sum :: ([Double] -> Double) -> [Double] -> Bool
-t_sum f xs = f xs == trueSum xs
-
-t_sum_error :: ([Double] -> Double) -> [Double] -> Bool
-t_sum_error f xs = abs (ts - f xs) <= abs (ts - Prelude.sum xs)
-  where ts = trueSum xs
-
-t_sum_shifted :: ([Double] -> Double) -> [Double] -> Bool
+-- Test that summation result is same as exact sum. That should pass
+-- if we're effectively working with quad precision
+t_sum :: ([Double] -> Double) -> [Double] -> Property
+t_sum f xs
+  = counterexample ("APPROX = " ++ show approx)
+  $ counterexample ("EXACT  = " ++ show exact)
+  $ counterexample ("DELTA  = " ++ show (approx - exact))
+  $ counterexample ("ULPS   = " ++ show (ulpDistance approx exact))
+  $ approx == exact
+  where
+    approx = f xs
+    exact  = trueSum xs
+
+-- Test that summation has smaller error than naive summation or no
+-- worse than given number of ulps. If we're close enough to exact
+-- answer naive may get ahead
+t_sum_error :: ([Double] -> Double) -> [Double] -> Property
+t_sum_error f xs
+  = counterexample ("APPROX  = " ++ show approx)
+  $ counterexample ("NAIVE   = " ++ show naive)
+  $ counterexample ("EXACT   = " ++ show exact)
+  $ counterexample ("A-EXACT = " ++ show (approx - exact))
+  $ counterexample ("N-EXACT = " ++ show (naive  - exact))
+  $ counterexample ("ULPS[A] = " ++ show (ulpDistance approx exact))
+  $ counterexample ("ULPS[N] = " ++ show (ulpDistance naive  exact))
+  $ abs (exact - approx) <= abs (exact - naive)
+  where
+    naive  = Prelude.sum xs
+    approx = f xs
+    exact  = trueSum xs
+
+t_sum_shifted :: ([Double] -> Double) -> [Double] -> Property
 t_sum_shifted f = t_sum_error f . zipWith (+) badvec
 
 trueSum :: (Fractional b, Real a) => [a] -> b
 trueSum xs = fromRational . Prelude.sum . map toRational $ xs
 
 badvec :: [Double]
-badvec = cycle [1,1e16,-1e16]
+badvec = cycle [1, 1e14, -1e14]
 
 tests :: TestTree
-tests = testGroup "Summation" [
-    testGroup "ID" [
-      -- plain summation loses precision quickly
-      -- testProperty "t_sum" $ t_sum (sum id)
-
-      -- tautological tests:
-      -- testProperty "t_sum_error" $ t_sum_error (sum id)
-      -- testProperty "t_sum_shifted" $ t_sum_shifted (sum id)
-    ]
-  , testGroup "Kahan" [
-      -- tests that cannot pass:
-      -- testProprty "t_sum" $ t_sum (sum kahan)
-      -- testProperty "t_sum_error" $ t_sum_error (sum kahan)
-
-      -- kahan summation only beats normal summation with large values
+tests = testGroup "Summation"
+  [ testGroup "Kahan" [
+      -- Kahan summation only beats naive summation when truly
+      -- catastrophic cancellation occurs
       testProperty "t_sum_shifted" $ t_sum_shifted (sum kahan)
     ]
   , testGroup "KBN" [
-      testProperty "t_sum" $ t_sum (sum kbn)
-    , testProperty "t_sum_error" $ t_sum_error (sum kbn)
+      testProperty "t_sum"         $ t_sum         (sum kbn)
+    , testProperty "t_sum_error"   $ t_sum_error   (sum kbn)
     , testProperty "t_sum_shifted" $ t_sum_shifted (sum kbn)
     ]
   , testGroup "KB2" [
-      testProperty "t_sum" $ t_sum (sum kb2)
-    , testProperty "t_sum_error" $ t_sum_error (sum kb2)
+      testProperty "t_sum"         $ t_sum         (sum kb2)
+    , testProperty "t_sum_error"   $ t_sum_error   (sum kb2)
     , testProperty "t_sum_shifted" $ t_sum_shifted (sum kb2)
     ]
   ]