packages feed

aern2-mp-0.2.2.0: src/AERN2/MP/Accuracy.hs

{-|
    Module      :  AERN2.MP.Accuracy
    Description :  Rough accuracy of an enclosure
    Copyright   :  (c) Michal Konecny
    License     :  BSD3

    Maintainer  :  mikkonecny@gmail.com
    Stability   :  experimental
    Portability :  portable

    A type for measuring the accuracy of an enclosing set,
    roughly corresponding to the maximum absolute error in some distance metric
    approximately measured in bits.
-}
module AERN2.MP.Accuracy
    (Accuracy(NoInformation, Exact), bits, fromAccuracy,
     HasAccuracy(..),
     ac2prec,
     iterateUntilAccurate,
     convergentList2seqByAccuracy,
     seqByPrecision2seqByAccuracy,
     setPrecisionAtLeastAccuracy,
     ShowWithAccuracy(..),
     HasApproximate(..))
where

import MixedTypesNumPrelude
import qualified Prelude as P

import Control.CollectErrors

import Data.Complex

-- import Test.Hspec
import Test.QuickCheck ( Arbitrary(arbitrary), frequency )

import AERN2.Norm
import AERN2.Kleenean
import AERN2.MP.Precision

{- example -}

_example1 :: Accuracy
_example1 = 1 + 2*(bits 100)

{-| A non-negative Double value to serve as an error bound. Arithmetic is rounded towards +infinity. -}
data Accuracy = NoInformation | Bits { fromAccuracy :: Integer } | Exact
  deriving (P.Eq, P.Ord)

instance Arbitrary Accuracy where
  arbitrary =
    frequency
      [(int 1, pure Exact),
       (int 1, pure NoInformation),
       (int 8, Bits <$> arbitrary)]

instance Enum Accuracy where
    fromEnum NoInformation = minBound
    fromEnum (Bits i) = int i
    fromEnum Exact = maxBound
    toEnum i = Bits (integer i)

instance Bounded Accuracy where
    minBound = NoInformation
    maxBound = Exact

instance ConvertibleExactly Integer Accuracy where
  safeConvertExactly = Right . Bits
instance ConvertibleExactly Int Accuracy where
  safeConvertExactly = Right . Bits . integer
instance ConvertibleExactly Precision Accuracy where
  safeConvertExactly = Right . Bits . integer
instance ConvertibleExactly NormLog Accuracy where
  safeConvertExactly (NormBits b) = Right $ bits (-b)
  safeConvertExactly NormZero = Right Exact

bits :: (ConvertibleExactly t Accuracy) => t -> Accuracy
bits = convertExactly

instance Show Accuracy where
    show (NoInformation) = "NoInformation"
    show (Bits a) = "bits " ++ show a
    show (Exact) = "Exact"

instance HasEqAsymmetric Accuracy Accuracy
instance HasOrderAsymmetric Accuracy Accuracy
instance CanMinMaxAsymmetric Accuracy Accuracy

instance HasEqAsymmetric Accuracy Integer where
  equalTo = convertSecond equalTo
instance HasEqAsymmetric Integer Accuracy where
  equalTo = convertFirst equalTo
instance HasEqAsymmetric Accuracy Int where
  equalTo = convertSecond equalTo
instance HasEqAsymmetric Int Accuracy where
  equalTo = convertFirst equalTo

instance HasOrderAsymmetric Accuracy Integer where
  lessThan = convertSecond lessThan
  leq = convertSecond leq
instance HasOrderAsymmetric Integer Accuracy where
  lessThan = convertFirst lessThan
  leq = convertFirst leq
instance HasOrderAsymmetric Accuracy Int where
  lessThan = convertSecond lessThan
  leq = convertSecond leq
instance HasOrderAsymmetric Int Accuracy where
  lessThan = convertFirst lessThan
  leq = convertFirst leq

instance CanMinMaxAsymmetric Accuracy Integer where
    type MinMaxType Accuracy Integer = Accuracy
    min = convertSecond min
    max = convertSecond max
instance CanMinMaxAsymmetric Integer Accuracy where
    type MinMaxType Integer Accuracy = Accuracy
    min = convertFirst min
    max = convertFirst max
instance CanMinMaxAsymmetric Accuracy Int where
    type MinMaxType Accuracy Int = Accuracy
    min = convertSecond min
    max = convertSecond max
instance CanMinMaxAsymmetric Int Accuracy where
    type MinMaxType Int Accuracy = Accuracy
    min = convertFirst min
    max = convertFirst max

instance CanNeg Accuracy where
  negate NoInformation = Exact
  negate Exact = NoInformation
  negate (Bits a) = Bits (-a)

instance CanAddAsymmetric Accuracy Accuracy where
   add NoInformation _ = NoInformation
   add _ NoInformation = NoInformation
   add (Bits a) (Bits b) = Bits $ a + b
   add Exact _ = Exact
   add _ Exact = Exact

instance CanSub Accuracy Accuracy

--instance CanMulAsymmetric Accuracy Accuracy where
--    mulA NoInformation _ = NoInformation
--    mulA _ NoInformation = NoInformation
--    mulA (Bits a) (Bits b) = Bits $ a * b
--    mulA Exact _ = Exact
--    mulA _ Exact = Exact

instance CanMulAsymmetric Accuracy Integer where
    type MulType Accuracy Integer = Accuracy
    mul NoInformation _ = NoInformation
    mul (Bits a) i = Bits $ a * i
    mul Exact _ = Exact

instance CanMulAsymmetric Integer Accuracy where
    type MulType Integer Accuracy = Accuracy
    mul i a = mul a i

instance CanAddAsymmetric Accuracy Integer where
    type AddType Accuracy Integer = Accuracy
    add NoInformation _ = NoInformation
    add (Bits a) i = Bits $ a + i
    add Exact _ = Exact

instance CanAddAsymmetric Integer Accuracy where
    type AddType Integer Accuracy = Accuracy
    add i a = add a i

instance CanSub Accuracy Integer where
    type SubType Accuracy Integer = Accuracy
    sub NoInformation _ = NoInformation
    sub (Bits a) i = Bits $ a - i
    sub Exact _ = Exact

class HasAccuracy a where
  getAccuracy :: a -> Accuracy
  {-| Return accuracy, except when the element is Exact, return its nominal Precision dressed as Accuracy.
      This function is useful when we have a convergent sequence where all elements happen to be
      actually equal to the limit and we need the property that the sequence elements keep improving.
  -}
  getFiniteAccuracy :: a -> Accuracy
  default getFiniteAccuracy :: (HasPrecision a) => a -> Accuracy
  getFiniteAccuracy b =
      case getAccuracy b of
          Exact -> bits $ getPrecision b
          a -> a

instance (HasAccuracy a, CanBeErrors es) => HasAccuracy (CollectErrors es a) where
  getAccuracy (CollectErrors ma es) =
    case ma of
      Just a | not (hasCertainError es) -> getAccuracy a
      _ -> NoInformation
  getFiniteAccuracy (CollectErrors ma es) = 
    case ma of
      Just a | not (hasCertainError es) -> getFiniteAccuracy a
      _ -> NoInformation

instance HasAccuracy Int where getAccuracy _ = Exact; getFiniteAccuracy _ = NoInformation
instance HasAccuracy Integer where getAccuracy _ = Exact; getFiniteAccuracy _ = NoInformation
instance HasAccuracy Rational where getAccuracy _ = Exact; getFiniteAccuracy _ = NoInformation
instance HasAccuracy Bool where getAccuracy _ = Exact; getFiniteAccuracy _ = NoInformation
instance HasAccuracy Kleenean where getAccuracy _ = Exact; getFiniteAccuracy _ = NoInformation

instance HasAccuracy t => HasAccuracy (Complex t) where
  getAccuracy (a :+ i) =
    (getAccuracy a) `min` (getAccuracy i)
  getFiniteAccuracy (a :+ i) =
    (getFiniteAccuracy a) `min` (getFiniteAccuracy i)
  
instance HasAccuracy t => HasAccuracy [t] where
  getAccuracy xs = foldl min Exact $ map getAccuracy xs
  getFiniteAccuracy xs = foldl min Exact $ map getFiniteAccuracy xs

instance HasAccuracy t => HasAccuracy (Maybe t) where
  getAccuracy (Just x) = getAccuracy x
  getAccuracy _ = NoInformation
  getFiniteAccuracy (Just x) = getFiniteAccuracy x
  getFiniteAccuracy _ = NoInformation

iterateUntilAccurate ::
  (HasAccuracy t) =>
  Accuracy ->
  (Precision -> Maybe t) ->
  [(Precision, Maybe t)]
iterateUntilAccurate ac =
  iterateUntilOK (ac2prec ac) $ \maybeResult ->
    case maybeResult of
      Just result -> getAccuracy result >= ac
      _ -> False

ac2prec :: Accuracy -> Precision
ac2prec ac =
  case ac of
    Bits b -> prec (max 2 $ b + 50)
    _ -> prec 100

seqByPrecision2seqByAccuracy ::
    (HasAccuracy t) =>
    (Precision -> t) -> (Accuracy -> t)
seqByPrecision2seqByAccuracy seqByPrecision ac =
    convergentList2seqByAccuracy list ac
    where
    list =
      map seqByPrecision $ dropWhile (lowPrec ac) (standardPrecisions (ac2prec ac))
    lowPrec Exact _ = False
    lowPrec _ p = bits p < ac

convergentList2seqByAccuracy :: (HasAccuracy t) => [t] -> (Accuracy -> t)
convergentList2seqByAccuracy list ac = findAccurate list
  where
  findAccurate [] =
    error "convergentList2seqByAccuracy: the sequence either converges too slowly or it does not converge"
  findAccurate (b : rest)
    | getAccuracy b >= ac = b
    | otherwise = findAccurate rest

{-|
    Change the precision so that
    it is at least as high as the supplied accuracy
    (assuming the accuracy is finite).
-}
setPrecisionAtLeastAccuracy :: (HasPrecision t, CanSetPrecision t) => Accuracy -> t -> t
setPrecisionAtLeastAccuracy acc b
    | p_b < p_acc = setPrecision p_acc b
    | otherwise = b
    where
    p_acc =
        case acc of
          Exact -> error $ "setPrecisionAtLeastAccuracy: cannot match Exact accuracy"
          NoInformation -> p_b
          _ -> prec $ max 2 (fromAccuracy acc)
    p_b = getPrecision b

class ShowWithAccuracy t where
  showWithAccuracy :: Accuracy -> t -> String

{-| An unsafe approximation of an enclosure or exact value,
    useful mainly for showing something brief and readable to humans.
-}
class HasApproximate t where
    type Approximate t
    getApproximate :: Accuracy -> t -> (Approximate t)