{-| 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 roughly measuring the accuracy of an enclosure. -} module AERN2.MP.Accuracy (Accuracy(NoInformation, Exact), bits, fromAccuracy, HasAccuracy(..), HasAccuracyGuide(..), CanSetAccuracyGuide(..), adjustAccuracyGuide, getFiniteAccuracy, ac2prec, CanReduceSizeUsingAccuracyGuide(..), specCanReduceSizeUsingAccuracyGuide, iterateUntilAccurate, convergentList2CauchySeq, seqByPrecision2CauchySeq, setPrecisionAtLeastAccuracy, HasApproximate(..)) where import MixedTypesNumPrelude import qualified Prelude as P import Control.CollectErrors import Data.Complex import Test.Hspec import Test.QuickCheck import AERN2.Norm 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 instance (HasAccuracy a, SuitableForCE es) => HasAccuracy (CollectErrors es a) where getAccuracy (CollectErrors ma es) = case ma of Just a | not (hasCertainError es) -> getAccuracy a _ -> NoInformation instance HasAccuracy Int where getAccuracy _ = Exact instance HasAccuracy Integer where getAccuracy _ = Exact instance HasAccuracy Rational where getAccuracy _ = Exact instance HasAccuracy Bool where getAccuracy _ = Exact instance HasAccuracy t => HasAccuracy (Complex t) where getAccuracy (a :+ i) = (getAccuracy a) `min` (getAccuracy i) instance HasAccuracy t => HasAccuracy [t] where getAccuracy xs = foldl min Exact $ map getAccuracy xs instance HasAccuracy t => HasAccuracy (Maybe t) where getAccuracy (Just x) = getAccuracy x getAccuracy _ = NoInformation class HasAccuracyGuide a where getAccuracyGuide :: a -> Accuracy class HasAccuracyGuide a => CanSetAccuracyGuide a where setAccuracyGuide :: Accuracy -> a -> a adjustAccuracyGuide :: (CanSetAccuracyGuide a) => (Accuracy -> Accuracy) -> a -> a adjustAccuracyGuide adj_acG a = setAccuracyGuide (adj_acG (getAccuracyGuide a)) a {-| 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 :: (HasAccuracy t, HasPrecision t) => t -> Accuracy getFiniteAccuracy b = case getAccuracy b of Exact -> bits $ getPrecision b a -> a 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 seqByPrecision2CauchySeq :: (HasAccuracy t) => (Precision -> t) -> (Accuracy -> t) seqByPrecision2CauchySeq seqByPrecision ac = convergentList2CauchySeq list ac where list = map seqByPrecision $ dropWhile (lowPrec ac) (standardPrecisions (ac2prec ac)) lowPrec Exact _ = False lowPrec _ p = bits p < ac convergentList2CauchySeq :: (HasAccuracy t) => [t] -> (Accuracy -> t) convergentList2CauchySeq list ac = findAccurate list where findAccurate [] = error "convergentList2CauchySeq: 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 :: (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 CanReduceSizeUsingAccuracyGuide t where reduceSizeUsingAccuracyGuide :: Accuracy -> t -> t specCanReduceSizeUsingAccuracyGuide :: ( CanReduceSizeUsingAccuracyGuide t , HasEqCertainly t t , Arbitrary t, Show t) => (T t) -> Spec specCanReduceSizeUsingAccuracyGuide (T tName :: T t) = describe ("CanReduceSizeUsingAccuracyGuide " ++ tName) $ do it "is safe" $ property $ \ (t :: t) (ac :: Accuracy) -> reduceSizeUsingAccuracyGuide ac t ?==? t {-| 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)