horde-ad-0.3.0.0: src/HordeAd/Core/AstMethodShare.hs
{-# LANGUAGE CPP #-}
#if MIN_VERSION_GLASGOW_HASKELL(9,12,1,0)
{-# OPTIONS_GHC -fno-expose-overloaded-unfoldings #-}
#endif
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | Arithmetic instances for AST with sharing method AstMethodShare.
module HordeAd.Core.AstMethodShare
( cAstConvDownKFromS, cAstConvDownSFromR, cAstConvDownSFromX
, cAstConvUpSFromK, cAstConvUpRFromS, cAstConvUpXFromS
, astShareNoSimplify
) where
import Prelude
import Data.Proxy (Proxy (Proxy))
import Data.Type.Equality (gcastWith, testEquality, (:~:) (Refl))
import System.IO.Unsafe (unsafePerformIO)
import Data.Array.Nested (MapJust)
import Data.Array.Nested qualified as Nested
import Data.Array.Nested.Convert (withShsFromShR, withShsFromShX)
import Data.Array.Nested.Lemmas
import Data.Array.Nested.Mixed.Shape
import Data.Array.Nested.Shaped.Shape
import Data.Array.Nested.Types (unsafeCoerceRefl)
import HordeAd.Core.Ast
import HordeAd.Core.AstFreshId
import HordeAd.Core.AstTools
import HordeAd.Core.CarriersAst
import HordeAd.Core.CarriersConcrete
import HordeAd.Core.Conversion
import HordeAd.Core.OpsConcrete ()
import HordeAd.Core.TensorKind
import HordeAd.Core.Types
cAstConvDownKFromS :: forall r s.
AstTensor AstMethodShare s (TKS '[] r)
-> AstTensor AstMethodShare s (TKScalar r)
cAstConvDownKFromS (AstConvert (ConvCmp ConvXS (Conv0X STKScalar)) a) = a
cAstConvDownKFromS a = AstConvert (ConvCmp ConvX0 ConvSX) a
cAstConvDownSFromR :: forall sh x s.
ShS sh -> FullShapeTK x
-> AstTensor AstMethodShare s (TKR2 (Rank sh) x)
-> AstTensor AstMethodShare s (TKS2 sh x)
cAstConvDownSFromR sh _ (AstConvert (ConvCmp (ConvXR _x) ConvSX) a)
| let FTKS sh2 _ = ftkAst a
, Just Refl <- testEquality sh sh2 = a
cAstConvDownSFromR sh x t | Refl <- lemRankReplicate (Proxy @(Rank sh)) =
AstConvert (ConvCmp (ConvXS' (FTKS sh x)) ConvRX) t
cAstConvDownSFromX :: forall sh sh' x s. Rank sh ~ Rank sh'
=> ShS sh -> FullShapeTK x
-> AstTensor AstMethodShare s (TKX2 sh' x)
-> AstTensor AstMethodShare s (TKS2 sh x)
cAstConvDownSFromX sh _ (AstConvert (ConvCmp (ConvXX' (FTKX _sh' _x)) ConvSX) a)
| let FTKS sh2 _ = ftkAst a
, Just Refl <- testEquality sh sh2 = a
cAstConvDownSFromX sh x t = AstConvert (ConvXS' (FTKS sh x)) t
cAstConvUpSFromK :: forall r s. GoodScalar r
=> AstTensor AstMethodShare s (TKScalar r)
-> AstTensor AstMethodShare s (TKS '[] r)
cAstConvUpSFromK = AstConvert (ConvCmp ConvXS (Conv0X STKScalar))
cAstConvUpRFromS :: forall sh x s.
ShS sh -> FullShapeTK x
-> AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKR2 (Rank sh) x)
cAstConvUpRFromS sh x | Refl <- lemRankMapJust sh =
AstConvert (ConvCmp (ConvXR (ftkToSTK x)) ConvSX)
cAstConvUpXFromS :: forall sh sh' x s. Rank sh ~ Rank sh'
=> IShX sh' -> FullShapeTK x
-> AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKX2 sh' x)
cAstConvUpXFromS sh' x =
gcastWith (unsafeCoerceRefl :: Rank (MapJust sh) :~: Rank sh) $
AstConvert (ConvCmp (ConvXX' (FTKX sh' x)) ConvSX)
liftRFromS1 :: forall n x s.
(forall sh.
AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKS2 sh x))
-> AstTensor AstMethodShare s (TKR2 n x)
-> AstTensor AstMethodShare s (TKR2 n x)
{-# INLINE liftRFromS1 #-}
liftRFromS1 f a = case ftkAst a of
FTKR sh' x ->
withShsFromShR sh' $ \(sh :: ShS sh) ->
cAstConvUpRFromS sh x
$ f (cAstConvDownSFromR sh x a)
liftRFromS2 :: forall n x s.
(forall sh.
AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKS2 sh x))
-> AstTensor AstMethodShare s (TKR2 n x)
-> AstTensor AstMethodShare s (TKR2 n x)
-> AstTensor AstMethodShare s (TKR2 n x)
{-# INLINE liftRFromS2 #-}
liftRFromS2 f a b = case ftkAst a of
FTKR sh' x ->
withShsFromShR sh' $ \(sh :: ShS sh) ->
cAstConvUpRFromS sh x
$ f (cAstConvDownSFromR sh x a) (cAstConvDownSFromR sh x b)
liftXFromS1 :: forall sh' x s.
(forall sh.
AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKS2 sh x))
-> AstTensor AstMethodShare s (TKX2 sh' x)
-> AstTensor AstMethodShare s (TKX2 sh' x)
{-# INLINE liftXFromS1 #-}
liftXFromS1 f a = case ftkAst a of
FTKX sh' x ->
withShsFromShX sh' $ \(sh :: ShS sh) ->
cAstConvUpXFromS sh' x
$ f (cAstConvDownSFromX sh x a)
liftXFromS2 :: forall sh' x s.
(forall sh.
AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKS2 sh x)
-> AstTensor AstMethodShare s (TKS2 sh x))
-> AstTensor AstMethodShare s (TKX2 sh' x)
-> AstTensor AstMethodShare s (TKX2 sh' x)
-> AstTensor AstMethodShare s (TKX2 sh' x)
{-# INLINE liftXFromS2 #-}
liftXFromS2 f a b = case ftkAst a of
FTKX sh' x ->
withShsFromShX sh' $ \(sh :: ShS sh) ->
cAstConvUpXFromS sh' x
$ f (cAstConvDownSFromX sh x a) (cAstConvDownSFromX sh x b)
-- * Unlawful numeric instances for AST scalars; they are lawful modulo evaluation
-- The normal form has AstConcreteK or AstFromPlain (AstConcreteK),
-- if any, as the first argument of the constructor.
-- No flattening is performed beyond that.
instance (NumScalar r, KnownSpan s)
=> Num (AstTensor AstMethodShare s (TKScalar r)) where
(+) = AstPlusK
(*) = AstTimesK
negate = AstN1K NegateOp
abs = AstN1K AbsOp
signum = AstN1K SignumOp
{-# INLINE fromInteger #-}
fromInteger i = fromPlain $ AstConcreteK (fromInteger i)
instance (NumScalar r, IntegralH r, Nested.IntElt r, KnownSpan s)
=> IntegralH (AstTensor AstMethodShare s (TKScalar r)) where
quotH = AstI2K QuotOp
remH = AstI2K RemOp
instance (NumScalar r, Differentiable r, KnownSpan s)
=> Fractional (AstTensor AstMethodShare s (TKScalar r)) where
(/) = AstR2K DivideOp
recip = AstR1K RecipOp
{-# INLINE fromRational #-}
fromRational r = fromPlain $ AstConcreteK (fromRational r)
instance (NumScalar r, Differentiable r, KnownSpan s)
=> Floating (AstTensor AstMethodShare s (TKScalar r)) where
pi = error "pi is not defined for tensors"
exp = AstR1K ExpOp
log = AstR1K LogOp
sqrt = AstR1K SqrtOp
(**) = AstR2K PowerOp
logBase = AstR2K LogBaseOp
sin = AstR1K SinOp
cos = AstR1K CosOp
tan = AstR1K TanOp
asin = AstR1K AsinOp
acos = AstR1K AcosOp
atan = AstR1K AtanOp
sinh = AstR1K SinhOp
cosh = AstR1K CoshOp
tanh = AstR1K TanhOp
asinh = AstR1K AsinhOp
acosh = AstR1K AcoshOp
atanh = AstR1K AtanhOp
instance (NumScalar r, Differentiable r, KnownSpan s)
=> RealFloatH (AstTensor AstMethodShare s (TKScalar r)) where
atan2H = AstR2K Atan2Op
-- * Unlawful numeric instances for shaped AST; lawful modulo evaluation
instance NumScalar r
=> Num (AstTensor AstMethodShare s (TKS sh r)) where
(+) = AstPlusS
(*) = AstTimesS
negate = AstN1S NegateOp
abs = AstN1S AbsOp
signum = AstN1S SignumOp
fromInteger i = error $ "fromInteger is not defined for shaped tensors: "
++ show i
instance (NumScalar r, IntegralH r, Nested.IntElt r)
=> IntegralH (AstTensor AstMethodShare s (TKS sh r)) where
quotH = AstI2S QuotOp
remH = AstI2S RemOp
instance (NumScalar r, Differentiable r)
=> Fractional (AstTensor AstMethodShare s (TKS sh r)) where
(/) = AstR2S DivideOp
recip = AstR1S RecipOp
fromRational r = error $ "fromRational is not defined for shaped tensors: "
++ show r
instance (NumScalar r, Differentiable r)
=> Floating (AstTensor AstMethodShare s (TKS sh r)) where
pi = error "pi is not defined for tensors"
exp = AstR1S ExpOp
log = AstR1S LogOp
sqrt = AstR1S SqrtOp
(**) = AstR2S PowerOp
logBase = AstR2S LogBaseOp
sin = AstR1S SinOp
cos = AstR1S CosOp
tan = AstR1S TanOp
asin = AstR1S AsinOp
acos = AstR1S AcosOp
atan = AstR1S AtanOp
sinh = AstR1S SinhOp
cosh = AstR1S CoshOp
tanh = AstR1S TanhOp
asinh = AstR1S AsinhOp
acosh = AstR1S AcoshOp
atanh = AstR1S AtanhOp
instance (NumScalar r, Differentiable r)
=> RealFloatH (AstTensor AstMethodShare s (TKS sh r)) where
atan2H = AstR2S Atan2Op
-- * Unlawful instances of AST for bool; they are lawful modulo evaluation
instance Boolean (AstBool AstMethodShare) where
true = AstConcreteK True
false = AstConcreteK False
notB = AstBoolNotK
(&&*) = AstBoolAndK
b ||* c = notB (notB b &&* notB c)
-- Since u and v are duplicated here, they need to be shared.
-- We share their difference, which would most likely appear in the
-- inequalities once they are rewritten, to ensure it's shared and whatever
-- vectorization substitutes into it is shared as well.
-- Otherwise, if u and v are variables, the sharing would vanish
-- before vectoriation complicates the expression a bit, making it
-- worth sharing.
instance (KnownSpan s, NumScalar r)
=> EqH (AstTensor AstMethodShare s) (TKScalar r) where
vUnshared ==. uUnshared =
let uv = astShareNoSimplify (uUnshared - vUnshared)
in 0 <=. uv &&* uv <=. 0
instance (KnownSpan s, NumScalar r)
=> EqH (AstTensor AstMethodShare s) (TKS sh r) where
vUnshared ==. uUnshared =
let uv = astShareNoSimplify (uUnshared - vUnshared)
zero = fromPlain $ AstConcreteS $ defTargetRep $ ftkAst vUnshared
in zero <=. uv &&* uv <=. zero
instance (KnownSpan s, NumScalar r)
=> OrdH (AstTensor AstMethodShare s) (TKScalar r) where
v <=. u = AstLeqK (plainPart v) (plainPart u)
instance (KnownSpan s, NumScalar r)
=> OrdH (AstTensor AstMethodShare s) (TKS sh r) where
v <=. u = AstLeq (plainPart v) (plainPart u)
-- * Unlawful numeric instances for ranked AST; lawful modulo evaluation
instance NumScalar r
=> Num (AstTensor AstMethodShare s (TKR n r)) where
(+) = liftRFromS2 (+)
(-) = liftRFromS2 (-)
(*) = liftRFromS2 (*)
negate = liftRFromS1 negate
abs = liftRFromS1 abs
signum = liftRFromS1 signum
fromInteger i = error $ "fromInteger is not defined for ranked tensors: "
++ show i
instance (NumScalar r, IntegralH r, Nested.IntElt r)
=> IntegralH (AstTensor AstMethodShare s (TKR n r)) where
quotH = liftRFromS2 quotH
remH = liftRFromS2 remH
instance (NumScalar r, Differentiable r)
=> Fractional (AstTensor AstMethodShare s (TKR n r)) where
(/) = liftRFromS2 (/)
recip = liftRFromS1 recip
fromRational r = error $ "fromRational is not defined for ranked tensors: "
++ show r
instance (NumScalar r, Differentiable r)
=> Floating (AstTensor AstMethodShare s (TKR n r)) where
pi = error "pi is not defined for tensors"
exp = liftRFromS1 exp
log = liftRFromS1 log
sqrt = liftRFromS1 sqrt
(**) = liftRFromS2 (**)
logBase = liftRFromS2 logBase
sin = liftRFromS1 sin
cos = liftRFromS1 cos
tan = liftRFromS1 tan
asin = liftRFromS1 asin
acos = liftRFromS1 acos
atan = liftRFromS1 atan
sinh = liftRFromS1 sinh
cosh = liftRFromS1 cosh
tanh = liftRFromS1 tanh
asinh = liftRFromS1 asinh
acosh = liftRFromS1 acosh
atanh = liftRFromS1 atanh
instance (NumScalar r, Differentiable r)
=> RealFloatH (AstTensor AstMethodShare s (TKR n r)) where
atan2H = liftRFromS2 atan2H
instance (KnownSpan s, NumScalar r)
=> EqH (AstTensor AstMethodShare s) (TKR n r) where
v ==. u = case ftkAst v of
FTKR shv' x -> case ftkAst u of
FTKR shu' _ ->
withShsFromShR shv' $ \shv ->
withShsFromShR shu' $ \shu ->
case testEquality shv shu of
Just Refl ->
cAstConvDownSFromR shu x v ==. cAstConvDownSFromR shv x u
_ -> error $ "(==.): shapes don't match: "
++ show (shu, shv)
instance (KnownSpan s, NumScalar r)
=> OrdH (AstTensor AstMethodShare s) (TKR n r) where
v <=. u = case ftkAst v of
FTKR shv' x -> case ftkAst u of
FTKR shu' _ ->
withShsFromShR shv' $ \shv ->
withShsFromShR shu' $ \shu ->
case testEquality shv shu of
Just Refl ->
cAstConvDownSFromR shu x v <=. cAstConvDownSFromR shv x u
_ -> error $ "(<=.): shapes don't match: "
++ show (shu, shv)
-- * Unlawful numeric instances for mixed AST; lawful modulo evaluation
instance NumScalar r
=> Num (AstTensor AstMethodShare s (TKX sh r)) where
(+) = liftXFromS2 (+)
(-) = liftXFromS2 (-)
(*) = liftXFromS2 (*)
negate = liftXFromS1 negate
abs = liftXFromS1 abs
signum = liftXFromS1 signum
fromInteger i = error $ "fromInteger is not defined for mixed tensors: "
++ show i
instance (NumScalar r, IntegralH r, Nested.IntElt r)
=> IntegralH (AstTensor AstMethodShare s (TKX sh r)) where
quotH = liftXFromS2 quotH
remH = liftXFromS2 remH
instance (NumScalar r, Differentiable r)
=> Fractional (AstTensor AstMethodShare s (TKX sh r)) where
(/) = liftXFromS2 (/)
recip = liftXFromS1 recip
fromRational r = error $ "fromRational is not defined for mixed tensors: "
++ show r
instance (NumScalar r, Differentiable r)
=> Floating (AstTensor AstMethodShare s (TKX sh r)) where
pi = error "pi is not defined for tensors"
exp = liftXFromS1 exp
log = liftXFromS1 log
sqrt = liftXFromS1 sqrt
(**) = liftXFromS2 (**)
logBase = liftXFromS2 logBase
sin = liftXFromS1 sin
cos = liftXFromS1 cos
tan = liftXFromS1 tan
asin = liftXFromS1 asin
acos = liftXFromS1 acos
atan = liftXFromS1 atan
sinh = liftXFromS1 sinh
cosh = liftXFromS1 cosh
tanh = liftXFromS1 tanh
asinh = liftXFromS1 asinh
acosh = liftXFromS1 acosh
atanh = liftXFromS1 atanh
instance (NumScalar r, Differentiable r)
=> RealFloatH (AstTensor AstMethodShare s (TKX sh r)) where
atan2H = liftXFromS2 atan2H
instance (KnownSpan s, NumScalar r)
=> EqH (AstTensor AstMethodShare s) (TKX sh r) where
v ==. u = case ftkAst v of
FTKX shv' x -> case ftkAst u of
FTKX shu' _ ->
withShsFromShX shv' $ \shv ->
withShsFromShX shu' $ \shu ->
case testEquality shv shu of
Just Refl ->
cAstConvDownSFromX shu x v ==. cAstConvDownSFromX shv x u
_ -> error $ "(==.): shapes don't match: "
++ show (shu, shv)
instance (KnownSpan s, NumScalar r)
=> OrdH (AstTensor AstMethodShare s) (TKX sh r) where
v <=. u = case ftkAst v of
FTKX shv' x -> case ftkAst u of
FTKX shu' _ ->
withShsFromShX shv' $ \shv ->
withShsFromShX shu' $ \shu ->
case testEquality shv shu of
Just Refl ->
cAstConvDownSFromX shu x v <=. cAstConvDownSFromX shv x u
_ -> error $ "(<=.): shapes don't match: "
++ show (shu, shv)
-- * AstRaw instances
instance Boolean (AstRaw PlainSpan (TKScalar Bool)) where
true = AstRaw true
false = AstRaw false
notB b = AstRaw (notB $ unAstRaw b)
b &&* c = AstRaw (unAstRaw b &&* unAstRaw c)
b ||* c = AstRaw (unAstRaw b ||* unAstRaw c)
instance (EqH (AstTensor AstMethodShare s) y)
=> EqH (AstRaw s) y where
AstRaw v ==. AstRaw u = AstRaw $ v ==. u
instance (OrdH (AstTensor AstMethodShare s) y)
=> OrdH (AstRaw s) y where
AstRaw v <=. AstRaw u = AstRaw $ v <=. u
deriving instance Eq (AstRaw s y)
deriving instance Ord (AstRaw s y)
deriving instance Num (AstTensor AstMethodShare s y) => Num (AstRaw s y)
deriving instance IntegralH (AstTensor AstMethodShare s y)
=> IntegralH (AstRaw s y)
deriving instance Fractional (AstTensor AstMethodShare s y)
=> Fractional (AstRaw s y)
deriving instance Floating (AstTensor AstMethodShare s y)
=> Floating (AstRaw s y)
deriving instance RealFloatH (AstTensor AstMethodShare s y)
=> RealFloatH (AstRaw s y)
-- * Misc
astShareNoSimplify :: KnownSpan s
=> AstTensor AstMethodShare s y
-> AstTensor AstMethodShare s y
{-# NOINLINE astShareNoSimplify #-}
astShareNoSimplify a | astIsSmall True a = a
-- too important an optimization to skip
astShareNoSimplify a = case a of
AstFromPrimal v -> fromPrimal $ astShareNoSimplify v
AstFromDual v -> fromDual $ astShareNoSimplify v
AstFromPlain v -> fromPlain $ astShareNoSimplify v
_ -> unsafePerformIO $ case ftkAst a of
ftk@FTKScalar -> do
var <- funToAstAutoBoundsIO ftk a
pure $! astShare var a
FTKR sh' x ->
withShsFromShR sh' $ \(sh :: ShS sh) -> do
let v = cAstConvDownSFromR sh x a
var <- funToAstNoBoundsIO (FTKS sh x)
pure $! cAstConvUpRFromS sh x $ astShare var v
FTKX sh' x ->
withShsFromShX sh' $ \(sh :: ShS sh) -> do
let v = cAstConvDownSFromX sh x a
var <- funToAstNoBoundsIO (FTKS sh x)
pure $! cAstConvUpXFromS sh' x $ astShare var v
FTKS ZSS x@FTKScalar -> do
let v = cAstConvDownKFromS a
var <- funToAstAutoBoundsIO x v
pure $! cAstConvUpSFromK $ astShare var v
-- calling recursively for product may be not worth it
ftk -> do
var <- funToAstNoBoundsIO ftk
pure $! astShare var a