{-# LANGUAGE DeriveFunctor, DeriveFoldable, NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleInstances #-}
module Taylor where
-- playing around with ideas from https://iagoleal.com/posts/calculus-symbolic-ode/
-- my idea is to use the taylor series based definitions to give implementations for the BigDecimal Floating instance.
import Data.BigDecimal
import Data.BigFloating (piChudnovsky)
import Data.Foldable (toList)
--import Prelude hiding (pi)
defaultRounding :: RoundingAdvice
defaultRounding = (DOWN, Just 400)
data Stream a = a :> Stream a
deriving (Functor, Foldable)
infixr 2 :>
ex :: Num a => Stream a
ex = 1 :> ex
sine :: Num a => Stream a
sine = 0 :> cosine
cosine :: Num a => Stream a
cosine = 1 :> fmap negate sine
-- | Turn a Stream f into a functional approximation
-- of its Taylor series around a point a.
-- That is, eval a f ≈ f(a + x)
eval :: Fractional a => a -> Stream a -> a -> a
eval a f x = foldr1 (\ fa f' -> fa + (x - a) * f') (take 300 taylor)
where
taylor = zipWith (/) (toList f) factorials
factorials = let fats = 1 : zipWith (*) fats [1..]
in fmap fromIntegral fats
eval' :: BigDecimal -> Stream BigDecimal -> BigDecimal -> BigDecimal
eval' a f x = foldr1 (\ fa f' -> fa + (x - a) * f') (take 5000 taylor)
where
taylor = zipWith (/) (toList f) factorials
factorials = let fats = 1 : zipWith (*) fats [1..]
in fmap fromIntegral fats
-- | Taylor series representation of the derivative.
diff :: Stream a -> Stream a
diff (_ :> f') = f'
-- | Taylor series for the constant zero.
zero :: Num a => Stream a
zero = 0 :> zero
euler :: BigDecimal
euler = eval 0 ex 1
--pii = piChudnovsky defaultRounding
p :: BigDecimal
p = eval 0 pi 0
-- | Taylor series for the identity function `f x = x`.
x :: Num a => Stream a
x = 0 :> 1 :> zero
instance Num a => Num (Stream a) where
-- Good ol' linearity
(+) (fa :> f') (ga :> g') = fa + ga :> f' + g'
(-) (fa :> f') (ga :> g') = fa - ga :> f' - g'
negate = fmap negate
-- Leibniz rule applied to streams
(*) f@(fa :> f') g@(ga :> g') = fa * ga :> f' * g + f * g'
fromInteger n = fromInteger n :> zero
abs = error "Absolute value is not a smooth function"
signum = error "No well-defined sign for a series"
instance Fractional a => Fractional (Stream a) where
-- The division rule from Calculus. We assume g(0) ≠ 0
(/) f@(fa :> f') g@(ga :> g') = fa / ga :> (f' * g - f * g') / g^2
fromRational n = fromRational n :> zero
analytic :: Num a => (a -> a) -> (Stream a -> Stream a) -> Stream a -> Stream a
analytic g g' f@(fa :> f') = g fa :> g' f * f'
instance Floating (Stream BigDecimal) where
pi = piChudnovsky defaultRounding :> zero
exp = analytic exp exp
log = analytic log recip
sin = analytic sin cos
cos = analytic cos (negate . sin)
asin = analytic asin (\x -> 1 / sqrt (1 - x^2))
acos = analytic acos (\x -> -1 / sqrt (1 - x^2))
atan = analytic atan (\x -> 1 / (1 + x^2))
sinh = analytic sinh cosh
cosh = analytic cosh sinh
asinh = analytic asinh (\x -> 1 / sqrt (x^2 + 1))
acosh = analytic acosh (\x -> 1 / sqrt (x^2 - 1))
atanh = analytic atanh (\x -> 1 / (1 - x^2))