1 {-# LANGUAGE RebindableSyntax #-}
3 module Integration.Simpson
6 import Misc (partition)
8 import NumericPrelude hiding (abs)
9 import qualified Algebra.RealField as RealField
10 import qualified Algebra.ToInteger as ToInteger
11 import qualified Algebra.ToRational as ToRational
13 -- | Use the Simpson's rule to numerically integrate @f@ over the
14 -- interval [@a@, @b@].
19 -- >>> simpson_1 f (-1) 1
23 -- >>> simpson_1 f (-1) 1
26 -- >>> import Algebra.Absolute (abs)
28 -- >>> let area = simpson_1 f (-1) 1
29 -- >>> abs (area - (2/3)) < 1/10^12
33 -- >>> simpson_1 f (-1) 1
37 -- >>> simpson_1 f 0 1
40 simpson_1 :: (RealField.C a, ToRational.C a, RealField.C b)
41 => (a -> b) -- ^ The function @f@
42 -> a -- ^ The \"left\" endpoint, @a@
43 -> a -- ^ The \"right\" endpoint, @b@
46 coefficient * ((f a) + 4*(f midpoint) + (f b))
48 coefficient = (fromRational' $ toRational (b - a)) / 6
49 midpoint = (a + b) / 2
52 -- | Use the composite Simpson's rule to numerically integrate @f@
53 -- over @n@ subintervals of [@a@, @b@].
57 -- >>> import Algebra.Absolute (abs)
59 -- >>> let area = simpson 10 f (-1) 1
60 -- >>> abs (area - (2/5)) < 0.0001
63 -- Note that the convergence here is much faster than the Trapezoid
66 -- >>> import Algebra.Absolute (abs)
67 -- >>> let area = simpson 10 sin 0 pi
68 -- >>> abs (area - 2) < 0.00001
71 simpson :: (RealField.C a,
76 => c -- ^ The number of subintervals to use, @n@
77 -> (a -> b) -- ^ The function @f@
78 -> a -- ^ The \"left\" endpoint, @a@
79 -> a -- ^ The \"right\" endpoint, @b@
82 sum $ map simpson_pairs pieces
84 pieces = partition n a b
85 -- Convert the simpson_1 function into one that takes pairs
86 -- (a,b) instead of individual arguments 'a' and 'b'.
87 simpson_pairs = uncurry (simpson_1 f)