]> gitweb.michael.orlitzky.com - numerical-analysis.git/blob - src/ODE/IVP.hs
381ff8a9c98099519dae2e08454fa8f05260127c
[numerical-analysis.git] / src / ODE / IVP.hs
1 {-# LANGUAGE ScopedTypeVariables #-}
2
3 -- | Numerical solution of the initial value problem,
4 --
5 -- y'(x) = f(x, y(x))
6 -- y'(x0) = y0
7 --
8 -- for x in [x0, xN].
9 --
10
11 module ODE.IVP
12 where
13
14 import Misc (partition)
15
16
17 -- | A single iteration of Euler's method over the interval
18 -- [$x0$, $x0$+$h$].
19 --
20 -- Examples:
21 --
22 -- >>> let x0 = 0.0
23 -- >>> let y0 = 1.0
24 -- >>> let h = 1.0
25 -- >>> let f x y = y
26 -- >>> eulers_method1 x0 y0 f h
27 -- 2.0
28 --
29 eulers_method1 :: (RealFrac a, RealFrac b)
30 => a -- ^ x0, the initial point
31 -> b -- ^ y0, the initial value at x0
32 -> (a -> b -> b) -- ^ The function f(x,y)
33 -> a -- ^ The step size h.
34 -> b
35 eulers_method1 x0 y0 f h =
36 y0 + h'*y'
37 where
38 h' = fromRational $ toRational h
39 y' = (f x0 y0)
40
41
42 -- | Perform $n$ iterations of Euler's method over the interval [$x0$,
43 -- $xN$]. The step size `h` will be calculated automatically. A list
44 -- of y-values will be returned.
45 --
46 -- The explicit 'forall' in the type signature allows us to refer
47 -- back to the type variables 'a' and 'b' in the 'where' clause.
48 --
49 -- Examples:
50 --
51 -- >>> let x0 = 0.0
52 -- >>> let xN = 1.0
53 -- >>> let y0 = 1.0
54 -- >>> let f x y = y
55 -- >>> let ys = eulers_method x0 xN y0 f 10000
56 -- >>> let yN = head $ reverse ys
57 -- >>> abs ((exp 1) - yN) < 1/10^3
58 -- True
59 --
60 eulers_method :: forall a b c. (RealFrac a, RealFrac b, Integral c)
61 => a -- ^ x0, the initial point
62 -> b -- ^ y0, the initial value at x0
63 -> a -- ^ xN, the terminal point
64 -> (a -> b -> b) -- ^ The function f(x,y)
65 -> c -- ^ n, the number of intervals to use.
66 -> [b]
67 eulers_method x0 y0 xN f n =
68 go xs y0 f
69 where
70 xs = partition n x0 xN
71
72 -- The 'go' function actually does all the work. It takes a list
73 -- of intervals [(x0,x1), (x1, x2)...] and peels off the first
74 -- one. It then runs the single-step Euler's method on that
75 -- interval, and afterwards recurses down the rest of the list.
76 go :: [(a,a)] -> b -> (a -> b -> b) -> [b]
77 go [] _ _ = []
78 go ((x0,x1):rest) y0 f = y1 : (go rest y1 f)
79 where
80 y1 = eulers_method1 x0 y0 f (x1 - x0)