{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wall -Werror -fno-warn-orphans #-}
module Data.SBV.Core.Floating (
IEEEFloating(..), IEEEFloatConvertible(..)
, sFloatAsSWord32, sDoubleAsSWord64, sWord32AsSFloat, sWord64AsSDouble
, blastSFloat, blastSDouble
, sFloatAsComparableSWord32, sDoubleAsComparableSWord64
) where
import qualified Data.Numbers.CrackNum as CN (wordToFloat, wordToDouble, floatToWord, doubleToWord)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word8, Word16, Word32, Word64)
import Data.Proxy
import Data.SBV.Core.AlgReals (isExactRational)
import Data.SBV.Core.Data
import Data.SBV.Core.Model
import Data.SBV.Core.Symbolic (addSValOptGoal)
import Data.SBV.Utils.Numeric
class (SymVal a, RealFloat a) => IEEEFloating a where
fpAbs :: SBV a -> SBV a
fpNeg :: SBV a -> SBV a
fpAdd :: SRoundingMode -> SBV a -> SBV a -> SBV a
fpSub :: SRoundingMode -> SBV a -> SBV a -> SBV a
fpMul :: SRoundingMode -> SBV a -> SBV a -> SBV a
fpDiv :: SRoundingMode -> SBV a -> SBV a -> SBV a
fpFMA :: SRoundingMode -> SBV a -> SBV a -> SBV a -> SBV a
fpSqrt :: SRoundingMode -> SBV a -> SBV a
fpRem :: SBV a -> SBV a -> SBV a
fpRoundToIntegral :: SRoundingMode -> SBV a -> SBV a
fpMin :: SBV a -> SBV a -> SBV a
fpMax :: SBV a -> SBV a -> SBV a
fpIsEqualObject :: SBV a -> SBV a -> SBool
fpIsNormal :: SBV a -> SBool
fpIsSubnormal :: SBV a -> SBool
fpIsZero :: SBV a -> SBool
fpIsInfinite :: SBV a -> SBool
fpIsNaN :: SBV a -> SBool
fpIsNegative :: SBV a -> SBool
fpIsPositive :: SBV a -> SBool
fpIsNegativeZero :: SBV a -> SBool
fpIsPositiveZero :: SBV a -> SBool
fpIsPoint :: SBV a -> SBool
fpAbs = lift1 FP_Abs (Just abs) Nothing
fpNeg = lift1 FP_Neg (Just negate) Nothing
fpAdd = lift2 FP_Add (Just (+)) . Just
fpSub = lift2 FP_Sub (Just (-)) . Just
fpMul = lift2 FP_Mul (Just (*)) . Just
fpDiv = lift2 FP_Div (Just (/)) . Just
fpFMA = lift3 FP_FMA Nothing . Just
fpSqrt = lift1 FP_Sqrt (Just sqrt) . Just
fpRem = lift2 FP_Rem (Just fpRemH) Nothing
fpRoundToIntegral = lift1 FP_RoundToIntegral (Just fpRoundToIntegralH) . Just
fpMin = liftMM FP_Min (Just fpMinH) Nothing
fpMax = liftMM FP_Max (Just fpMaxH) Nothing
fpIsEqualObject = lift2B FP_ObjEqual (Just fpIsEqualObjectH) Nothing
fpIsNormal = lift1B FP_IsNormal fpIsNormalizedH
fpIsSubnormal = lift1B FP_IsSubnormal isDenormalized
fpIsZero = lift1B FP_IsZero (== 0)
fpIsInfinite = lift1B FP_IsInfinite isInfinite
fpIsNaN = lift1B FP_IsNaN isNaN
fpIsNegative = lift1B FP_IsNegative (\x -> x < 0 || isNegativeZero x)
fpIsPositive = lift1B FP_IsPositive (\x -> x >= 0 && not (isNegativeZero x))
fpIsNegativeZero x = fpIsZero x .&& fpIsNegative x
fpIsPositiveZero x = fpIsZero x .&& fpIsPositive x
fpIsPoint x = sNot (fpIsNaN x .|| fpIsInfinite x)
instance IEEEFloating Float
instance IEEEFloating Double
class SymVal a => IEEEFloatConvertible a where
fromSFloat :: SRoundingMode -> SFloat -> SBV a
fromSFloat = genericFromFloat
toSFloat :: SRoundingMode -> SBV a -> SFloat
default toSFloat :: Integral a => SRoundingMode -> SBV a -> SFloat
toSFloat = genericToFloat (Just . fromRational . fromIntegral)
fromSDouble :: SRoundingMode -> SDouble -> SBV a
fromSDouble = genericFromFloat
toSDouble :: SRoundingMode -> SBV a -> SDouble
default toSDouble :: Integral a => SRoundingMode -> SBV a -> SDouble
toSDouble = genericToFloat (Just . fromRational . fromIntegral)
genericFromFloat :: forall a r. (IEEEFloating a, IEEEFloatConvertible r)
=> SRoundingMode
-> SBV a
-> SBV r
genericFromFloat rm f = SBV (SVal kTo (Right (cache r)))
where kFrom = kindOf f
kTo = kindOf (Proxy @r)
r st = do msv <- sbvToSV st rm
xsv <- sbvToSV st f
newExpr st kTo (SBVApp (IEEEFP (FP_Cast kFrom kTo msv)) [xsv])
genericToFloat :: forall a r. (IEEEFloatConvertible a, IEEEFloating r)
=> (a -> Maybe r)
-> SRoundingMode
-> SBV a
-> SBV r
genericToFloat converter rm i
| Just w <- unliteral i, Just RoundNearestTiesToEven <- unliteral rm, Just result <- converter w
= literal result
| True
= SBV (SVal kTo (Right (cache r)))
where kFrom = kindOf i
kTo = kindOf (Proxy @r)
r st = do msv <- sbvToSV st rm
xsv <- sbvToSV st i
newExpr st kTo (SBVApp (IEEEFP (FP_Cast kFrom kTo msv)) [xsv])
instance IEEEFloatConvertible Int8
instance IEEEFloatConvertible Int16
instance IEEEFloatConvertible Int32
instance IEEEFloatConvertible Int64
instance IEEEFloatConvertible Word8
instance IEEEFloatConvertible Word16
instance IEEEFloatConvertible Word32
instance IEEEFloatConvertible Word64
instance IEEEFloatConvertible Integer
instance IEEEFloatConvertible Float where
toSFloat _ f = f
toSDouble = genericToFloat (Just . fp2fp)
fromSFloat _ f = f
fromSDouble rm f
| Just RoundNearestTiesToEven <- unliteral rm
, Just fv <- unliteral f
= literal (fp2fp fv)
| True
= genericFromFloat rm f
instance IEEEFloatConvertible Double where
toSFloat = genericToFloat (Just . fp2fp)
toSDouble _ d = d
fromSDouble _ d = d
fromSFloat rm d
| Just RoundNearestTiesToEven <- unliteral rm
, Just dv <- unliteral d
= literal (fp2fp dv)
| True
= genericFromFloat rm d
instance IEEEFloatConvertible AlgReal where
toSFloat = genericToFloat (\r -> if isExactRational r then Just (fromRational (toRational r)) else Nothing)
toSDouble = genericToFloat (\r -> if isExactRational r then Just (fromRational (toRational r)) else Nothing)
concEval1 :: SymVal a => Maybe (a -> a) -> Maybe SRoundingMode -> SBV a -> Maybe (SBV a)
concEval1 mbOp mbRm a = do op <- mbOp
v <- unliteral a
case unliteral =<< mbRm of
Nothing -> (Just . literal) (op v)
Just RoundNearestTiesToEven -> (Just . literal) (op v)
_ -> Nothing
concEval2 :: SymVal a => Maybe (a -> a -> a) -> Maybe SRoundingMode -> SBV a -> SBV a -> Maybe (SBV a)
concEval2 mbOp mbRm a b = do op <- mbOp
v1 <- unliteral a
v2 <- unliteral b
case unliteral =<< mbRm of
Nothing -> (Just . literal) (v1 `op` v2)
Just RoundNearestTiesToEven -> (Just . literal) (v1 `op` v2)
_ -> Nothing
concEval2B :: SymVal a => Maybe (a -> a -> Bool) -> Maybe SRoundingMode -> SBV a -> SBV a -> Maybe SBool
concEval2B mbOp mbRm a b = do op <- mbOp
v1 <- unliteral a
v2 <- unliteral b
case unliteral =<< mbRm of
Nothing -> (Just . literal) (v1 `op` v2)
Just RoundNearestTiesToEven -> (Just . literal) (v1 `op` v2)
_ -> Nothing
concEval3 :: SymVal a => Maybe (a -> a -> a -> a) -> Maybe SRoundingMode -> SBV a -> SBV a -> SBV a -> Maybe (SBV a)
concEval3 mbOp mbRm a b c = do op <- mbOp
v1 <- unliteral a
v2 <- unliteral b
v3 <- unliteral c
case unliteral =<< mbRm of
Nothing -> (Just . literal) (op v1 v2 v3)
Just RoundNearestTiesToEven -> (Just . literal) (op v1 v2 v3)
_ -> Nothing
addRM :: State -> Maybe SRoundingMode -> [SV] -> IO [SV]
addRM _ Nothing as = return as
addRM st (Just rm) as = do svm <- sbvToSV st rm
return (svm : as)
lift1 :: SymVal a => FPOp -> Maybe (a -> a) -> Maybe SRoundingMode -> SBV a -> SBV a
lift1 w mbOp mbRm a
| Just cv <- concEval1 mbOp mbRm a
= cv
| True
= SBV $ SVal k $ Right $ cache r
where k = kindOf a
r st = do sva <- sbvToSV st a
args <- addRM st mbRm [sva]
newExpr st k (SBVApp (IEEEFP w) args)
lift1B :: SymVal a => FPOp -> (a -> Bool) -> SBV a -> SBool
lift1B w f a
| Just v <- unliteral a = literal $ f v
| True = SBV $ SVal KBool $ Right $ cache r
where r st = do sva <- sbvToSV st a
newExpr st KBool (SBVApp (IEEEFP w) [sva])
lift2 :: SymVal a => FPOp -> Maybe (a -> a -> a) -> Maybe SRoundingMode -> SBV a -> SBV a -> SBV a
lift2 w mbOp mbRm a b
| Just cv <- concEval2 mbOp mbRm a b
= cv
| True
= SBV $ SVal k $ Right $ cache r
where k = kindOf a
r st = do sva <- sbvToSV st a
svb <- sbvToSV st b
args <- addRM st mbRm [sva, svb]
newExpr st k (SBVApp (IEEEFP w) args)
liftMM :: (SymVal a, RealFloat a) => FPOp -> Maybe (a -> a -> a) -> Maybe SRoundingMode -> SBV a -> SBV a -> SBV a
liftMM w mbOp mbRm a b
| Just v1 <- unliteral a
, Just v2 <- unliteral b
, not ((isN0 v1 && isP0 v2) || (isP0 v1 && isN0 v2))
, Just cv <- concEval2 mbOp mbRm a b
= cv
| True
= SBV $ SVal k $ Right $ cache r
where isN0 = isNegativeZero
isP0 x = x == 0 && not (isN0 x)
k = kindOf a
r st = do sva <- sbvToSV st a
svb <- sbvToSV st b
args <- addRM st mbRm [sva, svb]
newExpr st k (SBVApp (IEEEFP w) args)
lift2B :: SymVal a => FPOp -> Maybe (a -> a -> Bool) -> Maybe SRoundingMode -> SBV a -> SBV a -> SBool
lift2B w mbOp mbRm a b
| Just cv <- concEval2B mbOp mbRm a b
= cv
| True
= SBV $ SVal KBool $ Right $ cache r
where r st = do sva <- sbvToSV st a
svb <- sbvToSV st b
args <- addRM st mbRm [sva, svb]
newExpr st KBool (SBVApp (IEEEFP w) args)
lift3 :: SymVal a => FPOp -> Maybe (a -> a -> a -> a) -> Maybe SRoundingMode -> SBV a -> SBV a -> SBV a -> SBV a
lift3 w mbOp mbRm a b c
| Just cv <- concEval3 mbOp mbRm a b c
= cv
| True
= SBV $ SVal k $ Right $ cache r
where k = kindOf a
r st = do sva <- sbvToSV st a
svb <- sbvToSV st b
svc <- sbvToSV st c
args <- addRM st mbRm [sva, svb, svc]
newExpr st k (SBVApp (IEEEFP w) args)
sFloatAsSWord32 :: SFloat -> SWord32
sFloatAsSWord32 fVal
| Just f <- unliteral fVal, not (isNaN f)
= literal (CN.floatToWord f)
| True
= SBV (SVal w32 (Right (cache y)))
where w32 = KBounded False 32
y st = do cg <- isCodeGenMode st
if cg
then do f <- sbvToSV st fVal
newExpr st w32 (SBVApp (IEEEFP (FP_Reinterpret KFloat w32)) [f])
else do n <- internalVariable st w32
ysw <- newExpr st KFloat (SBVApp (IEEEFP (FP_Reinterpret w32 KFloat)) [n])
internalConstraint st False [] $ unSBV $ fVal `fpIsEqualObject` SBV (SVal KFloat (Right (cache (\_ -> return ysw))))
return n
sDoubleAsSWord64 :: SDouble -> SWord64
sDoubleAsSWord64 fVal
| Just f <- unliteral fVal, not (isNaN f)
= literal (CN.doubleToWord f)
| True
= SBV (SVal w64 (Right (cache y)))
where w64 = KBounded False 64
y st = do cg <- isCodeGenMode st
if cg
then do f <- sbvToSV st fVal
newExpr st w64 (SBVApp (IEEEFP (FP_Reinterpret KDouble w64)) [f])
else do n <- internalVariable st w64
ysw <- newExpr st KDouble (SBVApp (IEEEFP (FP_Reinterpret w64 KDouble)) [n])
internalConstraint st False [] $ unSBV $ fVal `fpIsEqualObject` SBV (SVal KDouble (Right (cache (\_ -> return ysw))))
return n
blastSFloat :: SFloat -> (SBool, [SBool], [SBool])
blastSFloat = extract . sFloatAsSWord32
where extract x = (sTestBit x 31, sExtractBits x [30, 29 .. 23], sExtractBits x [22, 21 .. 0])
blastSDouble :: SDouble -> (SBool, [SBool], [SBool])
blastSDouble = extract . sDoubleAsSWord64
where extract x = (sTestBit x 63, sExtractBits x [62, 61 .. 52], sExtractBits x [51, 50 .. 0])
sWord32AsSFloat :: SWord32 -> SFloat
sWord32AsSFloat fVal
| Just f <- unliteral fVal = literal $ CN.wordToFloat f
| True = SBV (SVal KFloat (Right (cache y)))
where y st = do xsv <- sbvToSV st fVal
newExpr st KFloat (SBVApp (IEEEFP (FP_Reinterpret (kindOf fVal) KFloat)) [xsv])
sWord64AsSDouble :: SWord64 -> SDouble
sWord64AsSDouble dVal
| Just d <- unliteral dVal = literal $ CN.wordToDouble d
| True = SBV (SVal KDouble (Right (cache y)))
where y st = do xsv <- sbvToSV st dVal
newExpr st KDouble (SBVApp (IEEEFP (FP_Reinterpret (kindOf dVal) KDouble)) [xsv])
sFloatAsComparableSWord32 :: SFloat -> SWord32
sFloatAsComparableSWord32 f = ite (fpIsNegativeZero f) (sFloatAsComparableSWord32 0) (fromBitsBE $ sNot sb : ite sb (map sNot rest) rest)
where (sb : rest) = blastBE $ sFloatAsSWord32 f
sDoubleAsComparableSWord64 :: SDouble -> SWord64
sDoubleAsComparableSWord64 d = ite (fpIsNegativeZero d) (sDoubleAsComparableSWord64 0) (fromBitsBE $ sNot sb : ite sb (map sNot rest) rest)
where (sb : rest) = blastBE $ sDoubleAsSWord64 d
instance Metric Float where
type MetricSpace Float = Word32
toMetricSpace = sFloatAsComparableSWord32
fromMetricSpace = sWord32AsSFloat
msMinimize nm o = do constrain $ sNot $ fpIsNaN o
addSValOptGoal $ unSBV `fmap` Minimize nm (toMetricSpace o)
msMaximize nm o = do constrain $ sNot $ fpIsNaN o
addSValOptGoal $ unSBV `fmap` Maximize nm (toMetricSpace o)
instance Metric Double where
type MetricSpace Double = Word64
toMetricSpace = sDoubleAsComparableSWord64
fromMetricSpace = sWord64AsSDouble
msMinimize nm o = do constrain $ sNot $ fpIsNaN o
addSValOptGoal $ unSBV `fmap` Minimize nm (toMetricSpace o)
msMaximize nm o = do constrain $ sNot $ fpIsNaN o
addSValOptGoal $ unSBV `fmap` Maximize nm (toMetricSpace o)
{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}