{-# LANGUAGE RankNTypes#-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE DataKinds, ScopedTypeVariables #-}
module Numeric.AffineForm.Internal where
import Control.Monad.State hiding (fix)
import Control.Monad.Identity hiding (fix)
import Control.Exception (Exception, throw, evaluate, try)
import Numeric.AffineForm.Utils
import Numeric.AffineForm.ExplicitRounding
import qualified Numeric.Interval as IA
import Numeric.Interval ((...))
import Data.Fixed (mod')
import Data.Ratio (approxRational, (%))
import Data.Either (fromLeft, fromRight)
data AF s a
= AF a [a] a
deriving (Show)
data Curvature = Convex | Concave
data AFException
= DivisionByZero
| LogFromNegative
| AddingNegativeError
instance Show AFException where
show DivisionByZero = "division by zero"
show LogFromNegative = "logarithm from a negative number"
show AddingNegativeError = "cannot add a negative error to an affine form"
instance Exception AFException
instance (Fractional a, ExplicitRounding a, Ord a) => Num (AF s a) where
(+) = add
(*) = multiply
abs = absAF
signum = signumAF
fromInteger = singleton . fromInteger
negate = negateAF
instance (Fractional a, ExplicitRounding a, Ord a) => Fractional (AF s a) where
recip = recipAF
fromRational = singleton . fromRational
instance (Floating a, RealFrac a, ExplicitRounding a, Ord a) => Floating (AF s a) where
pi = approxSingleton pi
exp = minrange exp exp Convex
log x
| inf x > 0 = minrange log recip Concave x
| otherwise = throw LogFromNegative
sin = sinAF
cos = cosAF
asin = minrange asin (\x -> 1/sqrt (1-x^2)) undefined
acos = minrange acos (\x -> -1/sqrt (1-x^2)) undefined
atan = minrange atan (\x -> 1/(x^2+1)) undefined
sinh = minrange sinh cosh undefined
cosh = minrange cosh sinh Convex
asinh = minrange asinh (\x -> 1/sqrt (x^2+1)) undefined
acosh = minrange acosh (\x -> 1/((sqrt (x-1))*(sqrt (x+1)))) Concave
atanh = minrange atanh (\x -> 1/(1-x^2)) undefined
type AFIndex = Int
newtype AFMT t s m a = AFMT {runAFMT :: s -> m (a, s)}
type AFM t a = AFMT t AFIndex Identity a
instance (Monad m) => Functor (AFMT t s m) where
fmap = liftM
instance (Monad m) => Applicative (AFMT t s m) where
pure = return
(<*>) = ap
instance (Monad m) => Monad (AFMT t s m) where
return a = AFMT $ \s -> return (a, s)
(AFMT x) >>= f = AFMT $ \s -> do
(v, s') <- x s
(AFMT x') <- return $ f v
x' s'
instance (Monad m) => MonadState s (AFMT t s m) where
get = AFMT $ \s -> return (s, s)
put s = AFMT $ \_ -> return ((), s)
instance MonadTrans (AFMT t s) where
lift c = AFMT $ \s -> c >>= (\x -> return (x, s))
newEps :: Num a => AFM t (AF t a)
newEps = do
idx <- get
put $ idx + 1
return $ AF 0 (replicate idx 0 ++ [1]) 0
newFromInterval :: (Eq a, Fractional a, ExplicitRounding a) => IA.Interval a -> AFM t (AF t a)
newFromInterval i = do
eps <- newEps
let mult = ((IA.width i) / 2) .* eps
return $ (IA.midpoint i) .+ mult
singleton :: (Num a) => a -> AF s a
singleton x = AF x [] 0
approxSingleton :: (ExplicitRounding a) => a -> AF s a
approxSingleton x = AF x [] $ eps x
evalAFM :: forall a b. (forall t. AFM t b) -> b
evalAFM (AFMT x) = fst . runIdentity $ x 0
radius :: (Num a, ExplicitRounding a) => AF s a -> a
radius (AF _ xs xe) = v
where v = xe +/ (sumup $ abs <$> xs)
midpoint :: AF s a -> a
midpoint (AF x _ _) = x
inf :: (Num a, ExplicitRounding a) => AF s a -> a
inf af = x - eps x
where x = (midpoint af) - (radius af)
sup :: (Num a, ExplicitRounding a) => AF s a -> a
sup af = x + eps x
where x = (midpoint af) + (radius af)
interval :: (Num a, Ord a, ExplicitRounding a) => AF s a -> IA.Interval a
interval af = (inf af)...(sup af)
member :: (Num a, Ord a, ExplicitRounding a) => a -> AF s a -> Bool
member x af = x `IA.member` (interval af)
epscount_ :: AF s a -> Int
epscount_ (AF _ xs _) = length xs
setMidpoint :: (Num a, ExplicitRounding a) => a -> AF s a -> AF s a
setMidpoint m (AF x xs xe) = AF m xs $ xe + eps m
addError :: (Num a, Ord a) => AF s a -> a -> AF s a
addError (AF x xs xe) e
| e >= 0 = AF x xs (xe + e)
| otherwise = throw AddingNegativeError
(.+) :: (Num a, ExplicitRounding a) => a -> AF s a -> AF s a
a .+ (AF x xs xe) = AF m xs (xe + rnd)
where m = x + a
rnd = eps $ a + xe
add :: (ExplicitRounding a, Num a, Ord a) => AF s a -> AF s a -> AF s a
(AF x xs xe) `add` (AF y ys ye) = addError af rnd
where zs = (uncurry (+)) <$> embed xs ys
af = AF (x + y) zs (xe +/ ye)
rnd = sumup $ (uncurry (+/)) <$> embed (eps <$> xs ++ [x]) (eps <$> ys ++ [y])
negateAF :: (Num a) => AF s a -> AF s a
negateAF (AF x xs xe) = AF (-x) (negate <$> xs) xe
multiply :: (Num a, Ord a, ExplicitRounding a) => AF s a -> AF s a -> AF s a
af1@(AF x xs xe) `multiply` af2@(AF y ys ye) = addError af rnd
where zs = uncurry (+) <$> embed ((y*) <$> xs) ((x*) <$> ys)
ze1 = sum $ liftM2 (*/) (abs <$> xs ++ [xe]) (abs <$> ys ++ [ye])
ze2 = (abs x */ ye) +/ (abs y */ xe)
af = AF (x * y) zs (ze1 +/ ze2)
rnd = sumup $ (uncurry (*/)) <$> liftM2 (,) (eps <$> xs ++ [x, xe]) (eps <$> ys ++ [y, ye])
(.*) :: (Eq a, Num a, Ord a, ExplicitRounding a) => a -> AF s a -> AF s a
a .* (AF x xs xe) = addError af rnd
where af = AF (a*x) ((a*) <$> xs) $ (a * xe)
rnd = sumup $ eps . (a */) <$> xs ++ [x, xe]
recipAF :: (Ord a, Fractional a, ExplicitRounding a) => AF s a -> AF s a
recipAF af
| low > 0 = minrange recip (\x -> -1/x^2) Convex af
| high < 0 = negateAF . recipAF $ negateAF af
| otherwise = throw DivisionByZero
where high = sup af
low = inf af
cosAF :: (Ord a, RealFrac a, Floating a, ExplicitRounding a) => AF s a -> AF s a
cosAF af
| radius af < pi = f af
| otherwise = AF 0 [] 1
where a = inf af `pmod'` (2*pi)
b = sup af `pmod'` (2*pi)
f x
| a < pi && b < pi || a > pi && b > pi = minrange cos (negate . sin) undefined af
| a < b = AF (rl - 1) [] rl
| otherwise = AF (1 - rh) [] rh
where rl = abs (1 + (max (cos a) (cos b)))/2
rh = abs (1 - (min (cos a) (cos b)))/2
sinAF :: (Ord a, RealFrac a, Floating a, ExplicitRounding a) => AF s a -> AF s a
sinAF af = cosAF ((-pi/2) .+ af)
absAF :: (Ord a, ExplicitRounding a, Fractional a) => AF s a -> AF s a
absAF af
| inf af >= 0 = af
| sup af <= 0 = -af
| otherwise = AF x [] x
where x = (max (abs . sup $ af) (abs . inf $ af))/2
signumAF :: (Ord a, Num a, ExplicitRounding a) => AF s a -> AF s a
signumAF af
| inf af >= 0 = AF 1 [] 0
| sup af <= 0 = AF (-1) [] 0
| otherwise = AF 0 [] 1
fix :: (Num a, Ord a, ExplicitRounding a) => AF s a -> [a] -> IA.Interval a
fix (AF x xs xe) vals = (l)...(h)
where em = embed xs vals
s = sum $ uncurry (*) <$> em
m = x + s
l = m - xe
h = m + xe
minrange :: (Fractional a, Ord a, ExplicitRounding a) => (a -> a) -> (a -> a) -> Curvature -> (AF s a -> AF s a)
minrange f f' curv = \af ->
let a = sup af
b = inf af
p = case curv of
Convex -> f' a
Concave -> f' b
q = ((f a)+(f b)-p*(a+b))/2
d = abs ((f a)-(f b)+p*(a-b))/2
rnd = eps $ (eps $ q +/ a */ p) + (eps $ q +/ b */ p)
af1 = q .+ (p .* af) `addError` (d + rnd)
in
addError af1 rnd