{-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-| Module : What4.SpecialFunctions Description : Utilities relating to special functions Copyright : (c) Galois, Inc 2021 License : BSD3 Maintainer : Rob Dockins Utilties for representing and handling certain \"special\" functions arising from analysis. Although many of these functions are most properly understood as complex valued functions on complex arguments, here we are primarily interested in their restriction to real-valued functions or their floating-point approximations. The functions considered here include functions from standard and hyperbolic trigonometry, exponential and logarithmic functions, etc. Some of these functions are defineable in terms of others (e.g. @tan(x) = sin(x)/cos(x)@ or expm1(x) = exp(x) - 1@) but are commonly implemented separately in math libraries for increased precision. Some popular constant values are also included. -} module What4.SpecialFunctions ( -- * Representation of special functions R , SpecialFunction(..) -- ** Symmetry properties of special functions , FunctionSymmetry(..) , specialFnSymmetry -- ** Packaging arguments to special functions , SpecialFnArg(..) , traverseSpecialFnArg , SpecialFnArgs(..) , traverseSpecialFnArgs -- ** Interval data for domain and range , RealPoint(..) , RealBound(..) , RealInterval(..) , specialFnDomain , specialFnRange ) where import Data.Kind (Type) import Data.Parameterized.Classes import qualified Data.Parameterized.Context as Ctx import Data.Parameterized.Context ( pattern (:>) ) import Data.Parameterized.Ctx import Data.Parameterized.TH.GADT import Data.Parameterized.TraversableFC -- | Some special functions exhibit useful symmetries in their arguments. -- A function @f@ is an odd function if @f(-x) = -f(x)@, and is even -- if @f(-x) = f(x)@. We extend this notion to arguments of more than -- one function by saying that a function is even/odd in its @i@th -- argument if it is even/odd when the other arguments are fixed. data FunctionSymmetry r = NoSymmetry | EvenFunction | OddFunction deriving (Show) -- | Phantom data index representing the real number line. -- Used for specifying the arity of special functions. data R -- | Data type for representing \"special\" functions. -- These include functions from standard and hyperbolic -- trigonometry, exponential and logarithmic functions, -- as well as other well-known mathematical functions. -- -- Generally, little solver support exists for such functions -- (although systems like dReal and Metatarski can prove some -- properties). Nonetheless, we may have some information about -- specific values these functions take, the domains on which they -- are defined, or the range of values their outputs may take, or -- specific relationships that may exists between these functions -- (e.g., trig identities). This information may, in some -- circumstances, be sufficent to prove properties of interest, even -- if the functions cannot be represented in their entirety. data SpecialFunction (args :: Ctx Type) where -- constant values involving Pi Pi :: SpecialFunction EmptyCtx -- pi HalfPi :: SpecialFunction EmptyCtx -- pi/2 QuarterPi :: SpecialFunction EmptyCtx -- pi/4 OneOverPi :: SpecialFunction EmptyCtx -- 1/pi TwoOverPi :: SpecialFunction EmptyCtx -- 2/pi TwoOverSqrt_Pi :: SpecialFunction EmptyCtx -- 2/sqrt(pi) -- constant root values Sqrt_2 :: SpecialFunction EmptyCtx -- sqrt(2) Sqrt_OneHalf :: SpecialFunction EmptyCtx -- sqrt(1/2) -- constant values involving exponentials and logarithms E :: SpecialFunction EmptyCtx -- e = exp(1) Log2_E :: SpecialFunction EmptyCtx -- log_2(e) Log10_E :: SpecialFunction EmptyCtx -- log_10(e) Ln_2 :: SpecialFunction EmptyCtx -- ln(2) Ln_10 :: SpecialFunction EmptyCtx -- ln(10) -- circular trigonometry functions Sin :: SpecialFunction (EmptyCtx ::> R) -- sin(x) Cos :: SpecialFunction (EmptyCtx ::> R) -- cos(x) Tan :: SpecialFunction (EmptyCtx ::> R) -- tan(x) = sin(x)/cos(x) Arcsin :: SpecialFunction (EmptyCtx ::> R) -- inverse sin Arccos :: SpecialFunction (EmptyCtx ::> R) -- inverse cos Arctan :: SpecialFunction (EmptyCtx ::> R) -- inverse tan -- hyperbolic trigonometry functions Sinh :: SpecialFunction (EmptyCtx ::> R) -- sinh(x) (hyperbolic sine) Cosh :: SpecialFunction (EmptyCtx ::> R) -- cosh(x) Tanh :: SpecialFunction (EmptyCtx ::> R) -- tanh(x) Arcsinh :: SpecialFunction (EmptyCtx ::> R) -- inverse sinh Arccosh :: SpecialFunction (EmptyCtx ::> R) -- inverse cosh Arctanh :: SpecialFunction (EmptyCtx ::> R) -- inverse tanh -- rectangular to polar coordinate conversion Hypot :: SpecialFunction (EmptyCtx ::> R ::> R) -- hypot(x,y) = sqrt(x^2 + y^2) Arctan2 :: SpecialFunction (EmptyCtx ::> R ::> R) -- atan2(y,x) = atan(y/x) -- exponential and logarithm functions Pow :: SpecialFunction (EmptyCtx ::> R ::> R) -- x^y Exp :: SpecialFunction (EmptyCtx ::> R) -- exp(x) Log :: SpecialFunction (EmptyCtx ::> R) -- ln(x) Expm1 :: SpecialFunction (EmptyCtx ::> R) -- exp(x) - 1 Log1p :: SpecialFunction (EmptyCtx ::> R) -- ln(1+x) -- base 2 exponential and logarithm Exp2 :: SpecialFunction (EmptyCtx ::> R) -- 2^x Log2 :: SpecialFunction (EmptyCtx ::> R) -- log_2(x) -- base 10 exponential and logarithm Exp10 :: SpecialFunction (EmptyCtx ::> R) -- 10^x Log10 :: SpecialFunction (EmptyCtx ::> R) -- log_10(x) instance Show (SpecialFunction args) where show fn = case fn of Pi -> "pi" HalfPi -> "halfPi" QuarterPi -> "quaterPi" OneOverPi -> "oneOverPi" TwoOverPi -> "twoOverPi" TwoOverSqrt_Pi -> "twoOverSqrt_Pi" Sqrt_2 -> "sqrt_2" Sqrt_OneHalf -> "sqrt_oneHalf" E -> "e" Log2_E -> "log2_e" Log10_E -> "log10_e" Ln_2 -> "ln_2" Ln_10 -> "ln_10" Sin -> "sin" Cos -> "cos" Tan -> "tan" Arcsin -> "arcsin" Arccos -> "arccos" Arctan -> "arctan" Sinh -> "sinh" Cosh -> "cosh" Tanh -> "tanh" Arcsinh -> "arcsinh" Arccosh -> "arccosh" Arctanh -> "arctanh" Hypot -> "hypot" Arctan2 -> "atan2" Pow -> "pow" Exp -> "exp" Log -> "ln" Expm1 -> "expm1" Log1p -> "log1p" Exp2 -> "exp2" Log2 -> "log2" Exp10 -> "exp10" Log10 -> "log10" -- | Values that can appear in the definition of domain and -- range intervals for special functions. data RealPoint = Zero | NegOne | PosOne | NegInf | PosInf | NegPi | PosPi | NegHalfPi | PosHalfPi instance Show RealPoint where show Zero = "0" show NegOne = "-1" show PosOne = "+1" show NegInf = "-∞" show PosInf = "+∞" show NegPi = "-π" show PosPi = "+π" show NegHalfPi = "-π/2" show PosHalfPi = "+π/2" -- | The endpoint of an interval, which may be inclusive or exclusive. data RealBound = Incl RealPoint | Excl RealPoint -- | An interval on real values, or a point. data RealInterval r where RealPoint :: SpecialFunction EmptyCtx -> RealInterval R RealInterval :: RealBound -> RealBound -> RealInterval R instance Show (RealInterval r) where show (RealPoint x) = show x show (RealInterval lo hi) = lostr ++ ", " ++ histr where lostr = case lo of Incl x -> "[" ++ show x Excl x -> "(" ++ show x histr = case hi of Incl x -> show x ++ "]" Excl x -> show x ++ ")" -- | Compute function symmetry information for the given special function. specialFnSymmetry :: SpecialFunction args -> Ctx.Assignment FunctionSymmetry args specialFnSymmetry fn = case fn of Pi -> Ctx.Empty HalfPi -> Ctx.Empty QuarterPi -> Ctx.Empty OneOverPi -> Ctx.Empty TwoOverPi -> Ctx.Empty TwoOverSqrt_Pi -> Ctx.Empty Sqrt_2 -> Ctx.Empty Sqrt_OneHalf -> Ctx.Empty E -> Ctx.Empty Log2_E -> Ctx.Empty Log10_E -> Ctx.Empty Ln_2 -> Ctx.Empty Ln_10 -> Ctx.Empty Sin -> Ctx.Empty :> OddFunction Cos -> Ctx.Empty :> EvenFunction Tan -> Ctx.Empty :> OddFunction Arcsin -> Ctx.Empty :> OddFunction Arccos -> Ctx.Empty :> NoSymmetry Arctan -> Ctx.Empty :> OddFunction Sinh -> Ctx.Empty :> OddFunction Cosh -> Ctx.Empty :> EvenFunction Tanh -> Ctx.Empty :> OddFunction Arcsinh -> Ctx.Empty :> OddFunction Arccosh -> Ctx.Empty :> NoSymmetry Arctanh -> Ctx.Empty :> OddFunction Pow -> Ctx.Empty :> NoSymmetry :> NoSymmetry Exp -> Ctx.Empty :> NoSymmetry Log -> Ctx.Empty :> NoSymmetry Expm1 -> Ctx.Empty :> NoSymmetry Log1p -> Ctx.Empty :> NoSymmetry Exp2 -> Ctx.Empty :> NoSymmetry Log2 -> Ctx.Empty :> NoSymmetry Exp10 -> Ctx.Empty :> NoSymmetry Log10 -> Ctx.Empty :> NoSymmetry Hypot -> Ctx.Empty :> EvenFunction :> EvenFunction Arctan2 -> Ctx.Empty :> OddFunction :> NoSymmetry -- | Compute the range of values that may be returned by the given special function -- as its arguments take on the possible values of its domain. This may include -- limiting values if the function's domain includes infinities; for example -- @exp(-inf) = 0@. specialFnRange :: SpecialFunction args -> RealInterval R specialFnRange fn = case fn of Pi -> RealPoint Pi HalfPi -> RealPoint HalfPi QuarterPi -> RealPoint QuarterPi OneOverPi -> RealPoint OneOverPi TwoOverPi -> RealPoint TwoOverPi TwoOverSqrt_Pi -> RealPoint TwoOverSqrt_Pi Sqrt_2 -> RealPoint Sqrt_2 Sqrt_OneHalf -> RealPoint Sqrt_OneHalf E -> RealPoint E Log2_E -> RealPoint Log2_E Log10_E -> RealPoint Log10_E Ln_2 -> RealPoint Ln_2 Ln_10 -> RealPoint Ln_10 Sin -> RealInterval (Incl NegOne) (Incl PosOne) Cos -> RealInterval (Incl NegOne) (Incl PosOne) Tan -> RealInterval (Incl NegInf) (Incl PosInf) Arcsin -> RealInterval (Incl NegHalfPi) (Incl PosHalfPi) Arccos -> RealInterval (Incl Zero) (Incl PosPi) Arctan -> RealInterval (Incl NegHalfPi) (Incl PosHalfPi) Sinh -> RealInterval (Incl NegInf) (Incl PosInf) Cosh -> RealInterval (Incl PosOne) (Incl PosInf) Tanh -> RealInterval (Incl NegOne) (Incl PosOne) Arcsinh -> RealInterval (Incl NegInf) (Incl PosInf) Arccosh -> RealInterval (Incl Zero) (Incl PosInf) Arctanh -> RealInterval (Incl NegInf) (Incl PosInf) Pow -> RealInterval (Incl NegInf) (Incl PosInf) Exp -> RealInterval (Incl Zero) (Incl PosInf) Log -> RealInterval (Incl NegInf) (Incl PosInf) Expm1 -> RealInterval (Incl NegOne) (Incl PosInf) Log1p -> RealInterval (Incl NegInf) (Incl PosInf) Exp2 -> RealInterval (Incl Zero) (Incl PosInf) Log2 -> RealInterval (Incl NegInf) (Incl PosInf) Exp10 -> RealInterval (Incl Zero) (Incl PosInf) Log10 -> RealInterval (Incl NegInf) (Incl PosInf) Hypot -> RealInterval (Incl Zero) (Incl PosInf) Arctan2 -> RealInterval (Incl NegPi) (Incl PosPi) -- | Compute the domain of the given special function. As a mathematical -- entity, the value of the given function is not well-defined outside -- its domain. In floating-point terms, a special function will return -- a @NaN@ when evaluated on arguments outside its domain. specialFnDomain :: SpecialFunction args -> Ctx.Assignment RealInterval args specialFnDomain fn = case fn of Pi -> Ctx.Empty HalfPi -> Ctx.Empty QuarterPi -> Ctx.Empty OneOverPi -> Ctx.Empty TwoOverPi -> Ctx.Empty TwoOverSqrt_Pi -> Ctx.Empty Sqrt_2 -> Ctx.Empty Sqrt_OneHalf -> Ctx.Empty E -> Ctx.Empty Log2_E -> Ctx.Empty Log10_E -> Ctx.Empty Ln_2 -> Ctx.Empty Ln_10 -> Ctx.Empty Sin -> Ctx.Empty :> RealInterval (Excl NegInf) (Excl PosInf) Cos -> Ctx.Empty :> RealInterval (Excl NegInf) (Excl PosInf) Tan -> Ctx.Empty :> RealInterval (Excl NegInf) (Excl PosInf) Arcsin -> Ctx.Empty :> RealInterval (Incl NegOne) (Incl PosOne) Arccos -> Ctx.Empty :> RealInterval (Incl NegOne) (Incl PosOne) Arctan -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Sinh -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Cosh -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Tanh -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Arcsinh -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Arccosh -> Ctx.Empty :> RealInterval (Incl PosOne) (Incl PosInf) Arctanh -> Ctx.Empty :> RealInterval (Incl NegOne) (Incl PosOne) Pow -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) :> RealInterval (Incl NegInf) (Incl PosInf) Exp -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Log -> Ctx.Empty :> RealInterval (Incl Zero) (Incl PosInf) Expm1 -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Log1p -> Ctx.Empty :> RealInterval (Incl NegOne) (Incl PosInf) Exp2 -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Log2 -> Ctx.Empty :> RealInterval (Incl Zero) (Incl PosInf) Exp10 -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) Log10 -> Ctx.Empty :> RealInterval (Incl Zero) (Incl PosInf) Hypot -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) :> RealInterval (Incl NegInf) (Incl PosInf) Arctan2 -> Ctx.Empty :> RealInterval (Incl NegInf) (Incl PosInf) :> RealInterval (Incl NegInf) (Incl PosInf) -- | Data type for wrapping the actual arguments to special functions. data SpecialFnArg (e :: k -> Type) (tp::k) (r::Type) where SpecialFnArg :: e tp -> SpecialFnArg e tp R -- | Data type for wrapping a collction of actual arguments to special functions. newtype SpecialFnArgs (e :: k -> Type) (tp :: k) args = SpecialFnArgs (Ctx.Assignment (SpecialFnArg e tp) args) $(return []) instance HashableF SpecialFunction where hashWithSaltF = $(structuralHashWithSalt [t|SpecialFunction|] []) instance Hashable (SpecialFunction args) where hashWithSalt = hashWithSaltF instance TestEquality SpecialFunction where testEquality = $(structuralTypeEquality [t|SpecialFunction|] []) instance Eq (SpecialFunction args) where x == y = isJust (testEquality x y) instance OrdF SpecialFunction where compareF = $(structuralTypeOrd [t|SpecialFunction|] []) instance OrdF e => TestEquality (SpecialFnArg e tp) where testEquality (SpecialFnArg x) (SpecialFnArg y) = do Refl <- testEquality x y return Refl instance OrdF e => OrdF (SpecialFnArg e tp) where compareF (SpecialFnArg x) (SpecialFnArg y) = case compareF x y of LTF -> LTF EQF -> EQF GTF -> GTF instance HashableF e => HashableF (SpecialFnArg e tp) where hashWithSaltF s (SpecialFnArg x) = hashWithSaltF s x instance OrdF e => Eq (SpecialFnArgs e tp r) where SpecialFnArgs xs == SpecialFnArgs ys = xs == ys instance OrdF e => Ord (SpecialFnArgs e tp r) where compare (SpecialFnArgs xs) (SpecialFnArgs ys) = compare xs ys instance (HashableF e, OrdF e) => Hashable (SpecialFnArgs e tp args) where hashWithSalt s (SpecialFnArgs xs) = hashWithSaltF s xs traverseSpecialFnArg :: Applicative m => (e tp -> m (f tp)) -> SpecialFnArg e tp r -> m (SpecialFnArg f tp r) traverseSpecialFnArg f (SpecialFnArg x) = SpecialFnArg <$> f x traverseSpecialFnArgs :: Applicative m => (e tp -> m (f tp)) -> SpecialFnArgs e tp r -> m (SpecialFnArgs f tp r) traverseSpecialFnArgs f (SpecialFnArgs xs) = SpecialFnArgs <$> traverseFC (traverseSpecialFnArg f) xs