packages feed

hafar 0.1.0.0 → 0.1.1.0

raw patch · 4 files changed

+163/−18 lines, 4 filesdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Numeric.AffineForm: afError :: AF s a -> a
+ Numeric.AffineForm.Subdivision: SubdivisionEnvironment :: (a -> b) -> (b -> e) -> e -> Int -> Int -> SubdivisionEnvironment a b e
+ Numeric.AffineForm.Subdivision: [errorFun] :: SubdivisionEnvironment a b e -> b -> e
+ Numeric.AffineForm.Subdivision: [function] :: SubdivisionEnvironment a b e -> a -> b
+ Numeric.AffineForm.Subdivision: [maxDepth] :: SubdivisionEnvironment a b e -> Int
+ Numeric.AffineForm.Subdivision: [maxError] :: SubdivisionEnvironment a b e -> e
+ Numeric.AffineForm.Subdivision: [subdivs] :: SubdivisionEnvironment a b e -> Int
+ Numeric.AffineForm.Subdivision: branchAndBound :: (Subdivisible a, Subdivisible b, Ord e) => a -> SubdivisionEnvironment a b e -> b
+ Numeric.AffineForm.Subdivision: class Subdivisible a
+ Numeric.AffineForm.Subdivision: combine :: Subdivisible a => [a] -> a
+ Numeric.AffineForm.Subdivision: combine2 :: Subdivisible a => a -> a -> a
+ Numeric.AffineForm.Subdivision: data SubdivisionEnvironment a b e
+ Numeric.AffineForm.Subdivision: defaultEnvironment :: Fractional e => (a -> b) -> (b -> e) -> SubdivisionEnvironment a b e
+ Numeric.AffineForm.Subdivision: instance (GHC.Classes.Ord a, GHC.Real.Fractional a) => Numeric.AffineForm.Subdivision.Subdivisible (Numeric.Interval.Internal.Interval a)
+ Numeric.AffineForm.Subdivision: instance GHC.Base.Functor Numeric.AffineForm.Subdivision.SubdivisionTree
+ Numeric.AffineForm.Subdivision: instance GHC.Show.Show a => GHC.Show.Show (Numeric.AffineForm.Subdivision.SubdivisionTree a)
+ Numeric.AffineForm.Subdivision: instance Numeric.AffineForm.Subdivision.Subdivisible a => Numeric.AffineForm.Subdivision.Subdivisible [a]
+ Numeric.AffineForm.Subdivision: subdivide :: Subdivisible a => a -> Int -> [a]
- Numeric.AffineForm: evalAFM :: forall a b. (forall t. AFM t b) -> b
+ Numeric.AffineForm: evalAFM :: (forall t. AFM t b) -> b
- Numeric.AffineForm: type AFM t a = AFMT t AFIndex Identity a
+ Numeric.AffineForm: type AFM t a = AFMT t Identity a

Files

hafar.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 11f7d969e8f00b5e392eda2b0897debe20363e72178cbd6a8a87a7ab29e2b819+-- hash: 6bf5e0c8d0a1cae72591bdd92749728d178e27c39856dbdc12dc983ea2f322f6  name:           hafar-version:        0.1.0.0+version:        0.1.1.0 synopsis:       Affine arithmetic library for Haskell description:    Hafar is an affine arithmetic library for Haskell. It is an efficient way to work with ranges of values or imprecise values. category:       Numeric@@ -31,13 +31,14 @@   exposed-modules:       Numeric.AffineForm       Numeric.AffineForm.ExplicitRounding+      Numeric.AffineForm.Subdivision   other-modules:       Numeric.AffineForm.Utils       Numeric.AffineForm.Internal   hs-source-dirs:       src   build-depends:-      base >=4.12 && <4.14+      base >=4.12 && <4.13     , intervals >=0.8 && <0.9     , mtl >=2.2 && <2.3   default-language: Haskell2010@@ -50,13 +51,14 @@       Numeric.AffineForm.Internal       Numeric.AffineForm.Utils       Numeric.AffineForm.ExplicitRounding+      Numeric.AffineForm.Subdivision   hs-source-dirs:       test       src   ghc-options: -threaded -rtsopts -with-rtsopts=-N   build-depends:       QuickCheck >=2.13 && <2.14-    , base >=4.12 && <4.14+    , base >=4.12 && <4.13     , hafar     , intervals >=0.8 && <0.9     , mtl >=2.2 && <2.3
src/Numeric/AffineForm.hs view
@@ -6,12 +6,13 @@                            midpoint,                            inf, sup,                            interval,+                           afError,                            member,                            epscount_,                            setMidpoint,                            fix,                            addError,-                           (.+), (.*)+                           (.+), (.*),                           ) where  import Numeric.AffineForm.Internal
src/Numeric/AffineForm/Internal.hs view
@@ -15,6 +15,7 @@  import Numeric.AffineForm.Utils import Numeric.AffineForm.ExplicitRounding+import Numeric.AffineForm.Subdivision import qualified Numeric.Interval as IA import Numeric.Interval ((...)) import Data.Fixed (mod')@@ -69,32 +70,35 @@   acosh = minrange acosh (\x -> 1/((sqrt (x-1))*(sqrt (x+1)))) Concave   atanh = minrange atanh (\x -> 1/(1-x^2)) undefined +instance (Ord a, Fractional a, ExplicitRounding a) => Subdivisible (AF s a) where+  subdivide af n = mapToInterval af <$> subdivide (interval af) n+  combine2 l r   = fromInterval $ combine2 (interval l) (interval r)+ type AFIndex = Int  -- | AFM is a state monad that ensures that any new noise symbols have not been used by any previous affine form. -- All affine arithmetic calculations should be done inside the AFM monad. Affine forms do not make sense outside of their monad context.-newtype AFMT t s m a = AFMT {runAFMT :: s -> m (a, s)}-type AFM t a = AFMT t AFIndex Identity a+newtype AFMT t m a = AFMT {runAFMT :: AFIndex -> m (a, AFIndex)}+type AFM t a = AFMT t Identity a -instance (Monad m) => Functor (AFMT t s m) where+instance (Monad m) => Functor (AFMT t m) where   fmap = liftM -instance (Monad m) => Applicative (AFMT t s m) where+instance (Monad m) => Applicative (AFMT t m) where   pure = return   (<*>) = ap -instance (Monad m) => Monad (AFMT t s m) where+instance (Monad m) => Monad (AFMT t 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)+getIndex   = AFMT $ \s -> return (s, s)+putIndex s = AFMT $ \_ -> return ((), s) -instance MonadTrans (AFMT t s) where+instance MonadTrans (AFMT t) where   lift c = AFMT $ \s -> c >>= (\x -> return (x, s))  -- | This gives an affine form with midpoint 0 and radius 1.@@ -102,8 +106,8 @@ -- It can be used to instantiate new affine forms. newEps :: Num a => AFM t (AF t a) newEps = do-  idx <- get-  put $ idx + 1+  idx <- getIndex+  putIndex $ idx + 1   return $ AF 0 (replicate idx 0 ++ [1]) 0  -- | Creates a new affine form that covers the interval.@@ -119,12 +123,12 @@ singleton x = AF x [] 0  -- | Creates a new affine form that approximately represents some value.--- This function adds a small error to account for the 'wobble' in the computer representation of the value.+-- This function adds a small error to account for the `wobble` in the computer representation of the value. approxSingleton :: (ExplicitRounding a) => a -> AF s a approxSingleton x = AF x [] $ eps x  -- | Evaluates the AFM monad. It is not possible to get an AF out of an AFM monad.-evalAFM :: forall a b. (forall t. AFM t b) -> b+evalAFM :: (forall t. AFM t b) -> b evalAFM (AFMT x) = fst . runIdentity $ x 0  -- | Gives the radius of the affine form@@ -158,6 +162,10 @@ epscount_ :: AF s a -> Int epscount_ (AF _ xs _) = length xs +-- | Returns the value of the error term+afError :: AF s a -> a+afError (AF _ _ e) = e+ -- Affine arithmetic operations  -- | Sets the midpoint of the affine form@@ -169,6 +177,15 @@ addError (AF x xs xe) e   | e >= 0 = AF x xs (xe + e)   | otherwise = throw AddingNegativeError++mapToInterval :: (Fractional a, ExplicitRounding a) => AF s a -> IA.Interval a -> AF s a+mapToInterval af i = m .+ ((wi/waf) .* af)+  where waf = radius af+        wi  = IA.width i+        m   = IA.midpoint i++fromInterval :: (Fractional a) => IA.Interval a -> AF s a+fromInterval i = AF (IA.midpoint i) [] ((IA.width i)/2)  -- | Adds a scalar value to the affine form (.+) :: (Num a, ExplicitRounding a) => a -> AF s a -> AF s a
+ src/Numeric/AffineForm/Subdivision.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveFunctor #-}++-- | Subdivision contains an implementation of the branch-and-bound algorithm.+-- This method divides interval-like values into smaller subdivisions and then applies a function to those values.+-- This results in a more accurate output at the cost of having to repeat the calculations multiple times+module Numeric.AffineForm.Subdivision+  ( Subdivisible(..)+  , SubdivisionEnvironment(..)+  , defaultEnvironment+  , branchAndBound+  ) where++import Control.Monad.Reader+import Control.Monad.State++import Data.List++import Numeric.Interval as IA++-- | The 'Subdivisible' class is used for datatypes that can be broken down into smaller pieces+-- and combined together+--+-- The 'subdivide' function subdivides the value into `n` smaller values and 'combine' joins+-- the subdivisions together into a bigger value+--+-- Subdividing and then combining a value is expected to give the initial value (sans rounding errors)+class Subdivisible a where+  subdivide    :: a -> Int -> [a]+  combine2     :: a -> a -> a+  combine      :: [a] -> a++  combine2 l r = combine [l,r]+  combine      = foldl1 combine2++instance (Ord a, Fractional a) => Subdivisible (Interval a) where+  subdivide i n = f . fromIntegral <$> [1..n]+    where f x = (((x-1)*w/n')IA....(x*w/n')) + (IA.singleton $ inf i)+          w   = width i+          n'  = fromIntegral n+  combine2 = hull++instance (Subdivisible a) => Subdivisible [a] where+  subdivide l n = sequenceA $ flip subdivide n <$> l+  combine2 l r  = uncurry combine2 <$> zip l r++-- | A data structure for configuring the 'branchAndBound' method+--+-- 'function' is the function that gets evaluated+-- 'errorFun' is the error measuring function of the result+-- 'maxError' specifies the maximum permitted error of an evaluation+-- 'maxDepth' specifies how deep the subdivision can go+-- 'subdivs'  specifies the number of subdivisions per value+data SubdivisionEnvironment a b e = SubdivisionEnvironment+  { function :: a -> b+  , errorFun :: b -> e+  , maxError :: e+  , maxDepth :: Int+  , subdivs  :: Int+  }++-- | This function creates a simple subdivision configuration.+-- It requires the evaluator function and an error measuring function as its parameters.+defaultEnvironment :: (Fractional e) => (a -> b) -> (b -> e) -> SubdivisionEnvironment a b e+defaultEnvironment f g = SubdivisionEnvironment+  { function = f+  , errorFun = g+  , maxError = 0.1+  , maxDepth = 3+  , subdivs  = 2+  }++type Subdivider a b e = Reader (SubdivisionEnvironment a b e) (SubdivisionTree a)++data SubdivisionTree a+  = Branch [SubdivisionTree a]+  | Node a+  deriving (Show, Functor)++deepen :: (Subdivisible a, Ord e) => SubdivisionTree a -> Int -> Subdivider a b e+deepen (Node x) depth =+  do+    env <- ask+    let res = function env $ x+        err = errorFun env $ res+        n   = subdivs env+    if err <= maxError env+       -- Accurate answer found+       then return $ Node x+       -- Subdivide and deepen+       else deepen (Branch $ Node <$> subdivide x n) $ depth+deepen (Branch l) depth =+  do+    env <- ask+    if depth >= maxDepth env+       then return $ Branch l+       else Branch <$> (sequence $ flip deepen (depth + 1) <$> l)++collapse :: (Subdivisible a) => SubdivisionTree a -> a+collapse (Node a)   = a+collapse (Branch l) = combine $ collapse <$> l++evalTree :: (Subdivisible b) => Subdivider a b e -> SubdivisionEnvironment a b e -> b+evalTree div cfg = flip runReader cfg $+  do+    val <- div+    env <- ask+    return . collapse $ function env <$> val++-- | This function iteratively subdivides a value to arrive at a more accurate result+branchAndBound :: (Subdivisible a, Subdivisible b, Ord e) => a -> SubdivisionEnvironment a b e -> b+branchAndBound x env = flip evalTree env $ deepen (Node x) 0++test :: Interval Double+test =+  flip evalTree env $ deepen (Node $ [3 IA.... 4, 1 IA....4]) 0+  where env = SubdivisionEnvironment+          { function = (\[x, y] -> x * y - x)+          , errorFun = width+          , maxError = 0.1+          , maxDepth = 5+          , subdivs  = 2+          }+