horde-ad-0.1.0.0: src/HordeAd/Core/Adaptor.hs
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}
-- | Adaptors for working with types of collections of tensors,
-- e.g., tuples, sized lists and user types of statically known size,
-- as long as they have the proper instances defined.
-- The collections are used as representations of the domains
-- of objective functions that become the codomains of the reverse
-- derivative functions and also to handle multiple arguments
-- and results of fold-like operations.
module HordeAd.Core.Adaptor
( AdaptableTarget(..), TermValue(..), DualNumberValue(..)
, ForgetShape(..), RandomValue(..)
, stkOfListR
-- * Helper classes and types
, Tups, NoShapeTensorKind
) where
import Prelude
import Data.Default
import Data.Proxy (Proxy (Proxy))
import Data.Type.Equality (gcastWith, (:~:))
import Data.Vector.Generic qualified as V
import Data.Vector.Strict qualified as Data.Vector
import GHC.TypeLits (KnownNat, OrderingI (..), cmpNat, type (-), type (<=?))
import System.Random
import Data.Array.Nested qualified as Nested
import Data.Array.Nested.Mixed.Shape
import Data.Array.Nested.Ranked.Shape
import Data.Array.Nested.Shaped.Shape
import Data.Array.Nested.Types (unsafeCoerceRefl)
import HordeAd.Core.Ast
import HordeAd.Core.CarriersADVal
import HordeAd.Core.CarriersConcrete
import HordeAd.Core.ConvertTensor
import HordeAd.Core.Ops
import HordeAd.Core.OpsAst ()
import HordeAd.Core.TensorKind
import HordeAd.Core.Types
-- * Adaptor classes
-- Inspired by adaptors from @tomjaguarpaw's branch.
--
-- | The class that makes it possible to treat @vals@ (e.g., a tuple of tensors)
-- as a @target@-based (e.g., concrete or symbolic) value
-- of tensor kind @X vals@.
class AdaptableTarget (target :: Target) vals where
type X vals :: TK -- ^ what tensor kind represents the collection
toTarget :: vals -> target (X vals)
-- ^ represent a collection of tensors
fromTarget :: target (X vals) -> vals
-- ^ recovers a collection of tensors from its canonical representation;
-- requires a duplicable argument
-- | An embedding of a concrete collection of tensors to a non-concrete
-- counterpart of the same shape and containing the same data.
class TermValue vals where
type Value vals = result | result -> vals
-- ^ a helper type, with the same general shape,
-- but possibly more concrete, e.g., arrays instead of terms,
-- where the injectivity is crucial to limit the number
-- of type applications the library user has to supply
fromValue :: Value vals -> vals -- ^ an embedding
-- | An embedding of a concrete collection of tensors to a non-concrete
-- counterpart of the same shape and containing the same data.
-- This variant is possible to define more often, but the associated
-- type family is not injective.
class DualNumberValue vals where
type DValue vals
-- ^ a helper type, with the same general shape,
-- but possibly more concrete, e.g., arrays instead of terms,
-- where the injectivity is hard to obtain, but is not so important,
-- because the type is not used in the best pipeline
fromDValue :: DValue vals -> vals -- ^ an embedding
-- | A helper class for for converting all tensors inside a type
-- from shaped to ranked. It's useful when a collection of parameters
-- is defined as shaped tensor for 'RandomValue' but then is going
-- to be used as ranked tensor to make type reconstruction easier.
class ForgetShape vals where
type NoShape vals
forgetShape :: vals -> NoShape vals
-- | A helper class for randomly generating initial parameters.
-- Only instance for collections of shaped tensors and scalars are possible,
-- because only then the shapes of the tensors to generate are known
-- from their types.
class RandomValue vals where
randomValue :: Double -> StdGen -> (vals, StdGen)
-- * Base instances
instance AdaptableTarget target (target y) where
type X (target y) = y
toTarget = id
fromTarget t = t
{-# SPECIALIZE instance AdaptableTarget Concrete (Concrete (TKS sh Double)) #-}
{-# SPECIALIZE instance AdaptableTarget Concrete (Concrete (TKS sh Float)) #-}
-- a failed attempt to specialize without -fpolymorphic-specialisation
instance KnownSTK y
=> TermValue (AstTensor AstMethodLet FullSpan y) where
type Value (AstTensor AstMethodLet FullSpan y) = Concrete y
fromValue t = tconcrete (tftkG (knownSTK @y) $ unConcrete t) t
instance (BaseTensor target, BaseTensor (PrimalOf target), KnownSTK y)
=> DualNumberValue (target y) where
type DValue (target y) = Concrete y
fromDValue t = tfromPrimal (knownSTK @y)
$ tconcrete (tftkG (knownSTK @y) $ unConcrete t) t
instance ForgetShape (target (TKScalar r)) where
type NoShape (target (TKScalar r)) = target (TKScalar r)
forgetShape = id
instance ForgetShape (target (TKR n r)) where
type NoShape (target (TKR n r)) = target (TKR n r)
forgetShape = id
instance (KnownShS sh, GoodScalar r, ConvertTensor target)
=> ForgetShape (target (TKS sh r)) where
type NoShape (target (TKS sh r)) = target (TKR (Rank sh) r)
forgetShape = rfromS
instance ForgetShape (target (TKX sh r)) where
type NoShape (target (TKX sh r)) = target (TKX sh r)
forgetShape = id
type family NoShapeTensorKind tk where
NoShapeTensorKind (TKScalar r) = TKScalar r
NoShapeTensorKind (TKR2 n r) = TKR2 n r
NoShapeTensorKind (TKS2 sh r) = TKR2 (Rank sh) r
NoShapeTensorKind (TKX2 sh r) = TKX2 sh r
NoShapeTensorKind (TKProduct y z) =
TKProduct (NoShapeTensorKind y) (NoShapeTensorKind z)
instance ( ForgetShape (target a)
, ForgetShape (target b)
, target (NoShapeTensorKind a) ~ NoShape (target a)
, target (NoShapeTensorKind b) ~ NoShape (target b)
, BaseTensor target, LetTensor target )
=> ForgetShape (target (TKProduct a b)) where
type NoShape (target (TKProduct a b)) =
target (NoShapeTensorKind (TKProduct a b))
forgetShape ab =
ttlet ab $ \abShared ->
tpair (forgetShape (tproject1 abShared))
(forgetShape (tproject2 abShared))
instance forall r target. (GoodScalar r, BaseTensor target)
=> RandomValue (target (TKScalar r)) where
randomValue range g =
ifDifferentiable @r
(let (r, g2) = random g
m = 2 * realToFrac range * (r - 0.5)
in (tkconcrete m, g2))
(tkconcrete def, g)
instance forall sh r target. (KnownShS sh, GoodScalar r, BaseTensor target)
=> RandomValue (target (TKS sh r)) where
randomValue range g =
ifDifferentiable @r
(let createRandomVector :: Int -> StdGen -> target (TKS sh r)
createRandomVector n seed =
srepl (2 * realToFrac range)
* (tsconcrete
(Nested.sfromVector knownShS (V.fromListN n (randoms seed)))
- srepl 0.5)
(g1, g2) = splitGen g
arr = createRandomVector (shsSize (knownShS @sh)) g1
in (arr, g2))
(srepl def, g)
where srepl = tsconcrete . Nested.sreplicateScal knownShS
-- {-# SPECIALIZE instance (KnownShS sh, GoodScalar r, Fractional r, Random r) => RandomValue (Concrete (TKS sh r)) #-}
{-# SPECIALIZE instance KnownShS sh => RandomValue (Concrete (TKS sh Double)) #-}
{-# SPECIALIZE instance KnownShS sh => RandomValue (Concrete (TKS sh Float)) #-}
instance (RandomValue (target a), RandomValue (target b), BaseTensor target)
=> RandomValue (target (TKProduct a b)) where
randomValue range g =
let (v1, g1) = randomValue range g
(v2, g2) = randomValue range g1
in (tpair v1 v2, g2)
-- These instances are messy and hard to use, but we probably can't do better.
instance DualNumberValue Double where
type DValue Double = Concrete (TKScalar Double)
fromDValue (Concrete d) = d
instance DualNumberValue Float where
type DValue Float = Concrete (TKScalar Float)
fromDValue (Concrete d) = d
instance TermValue (Concrete (TKScalar Double)) where
type Value (Concrete (TKScalar Double)) = Double
fromValue = Concrete
instance TermValue (Concrete (TKScalar Float)) where
type Value (Concrete (TKScalar Float)) = Float
fromValue = Concrete
-- * Compound instances
instance (BaseTensor target, ConvertTensor target, GoodScalar r)
=> AdaptableTarget target [target (TKScalar r)] where
type X [target (TKScalar r)] = TKR 1 r
toTarget l = if null l
then trconcrete Nested.remptyArray
else trfromVector $ V.fromList $ map rfromK l
fromTarget = map kfromR . trunravelToList
-- inefficient, but we probably can't do better
instance (BaseTensor target, ConvertTensor target, GoodScalar r)
=> AdaptableTarget target
(Data.Vector.Vector (target (TKScalar r))) where
type X (Data.Vector.Vector (target (TKScalar r))) = TKR 1 r
toTarget v = if V.null v
then trconcrete Nested.remptyArray
else trfromVector $ V.map rfromK v
fromTarget =
V.fromList . map kfromR . trunravelToList
-- inefficient, but we probably can't do better
type family Tups n t where
Tups 0 t = TKUnit
Tups n t = TKProduct t (Tups (n - 1) t)
stkOfListR :: forall t n.
SingletonTK t -> SNat n -> SingletonTK (Tups n t)
stkOfListR _ (SNat' @0) = stkUnit
stkOfListR stk SNat =
gcastWith (unsafeCoerceRefl :: (1 <=? n) :~: True) $
gcastWith (unsafeCoerceRefl :: Tups n t :~: TKProduct t (Tups (n - 1) t)) $
STKProduct stk (stkOfListR stk (SNat @(n - 1)))
instance (BaseTensor target, KnownNat n, AdaptableTarget target a)
=> AdaptableTarget target (ListR n a) where
type X (ListR n a) = Tups n (X a)
toTarget ZR = tkconcrete Z1
toTarget ((:::) @n1 a rest) =
gcastWith (unsafeCoerceRefl
:: X (ListR n a) :~: TKProduct (X a) (X (ListR n1 a))) $
let a1 = toTarget a
rest1 = toTarget rest
in tpair a1 rest1
fromTarget tups = case SNat @n of
SNat' @0 -> ZR
_ ->
gcastWith (unsafeCoerceRefl :: (1 <=? n) :~: True) $
gcastWith (unsafeCoerceRefl
:: X (ListR n a) :~: TKProduct (X a) (X (ListR (n - 1) a))) $
let (a1, rest1) = (tproject1 tups, tproject2 tups)
a = fromTarget a1
rest = fromTarget rest1
in (a ::: rest)
{-# SPECIALIZE instance (KnownNat n, AdaptableTarget (AstTensor AstMethodLet FullSpan) a) => AdaptableTarget (AstTensor AstMethodLet FullSpan) (ListR n a) #-}
{-# SPECIALIZE instance (KnownNat n, AdaptableTarget (ADVal Concrete) a) => AdaptableTarget (ADVal Concrete) (ListR n a) #-}
instance TermValue a => TermValue [a] where
type Value [a] = [Value a]
fromValue = map fromValue
instance TermValue a => TermValue (Data.Vector.Vector a) where
type Value (Data.Vector.Vector a) = Data.Vector.Vector (Value a)
fromValue = V.map fromValue
instance TermValue a => TermValue (ListR n a) where
type Value (ListR n a) = ListR n (Value a)
fromValue ZR = ZR
fromValue (a ::: rest) = fromValue a ::: fromValue rest
instance DualNumberValue a => DualNumberValue [a] where
type DValue [a] = [DValue a]
fromDValue = map fromDValue
instance DualNumberValue a => DualNumberValue (Data.Vector.Vector a) where
type DValue (Data.Vector.Vector a) = Data.Vector.Vector (DValue a)
fromDValue = V.map fromDValue
instance DualNumberValue a => DualNumberValue (ListR n a) where
type DValue (ListR n a) = ListR n (DValue a)
fromDValue ZR = ZR
fromDValue (a ::: rest) = fromDValue a ::: fromDValue rest
instance ForgetShape [a] where
type NoShape [a] = [a]
forgetShape = id
instance ForgetShape (Data.Vector.Vector a) where
type NoShape (Data.Vector.Vector a) = Data.Vector.Vector a
forgetShape = id
instance ForgetShape a => ForgetShape (ListR n a) where
type NoShape (ListR n a) = ListR n (NoShape a)
forgetShape ZR = ZR
forgetShape (a ::: rest) = forgetShape a ::: forgetShape rest
instance (RandomValue a, KnownNat n) => RandomValue (ListR n a) where
randomValue range g = case cmpNat (Proxy @n) (Proxy @0) of
LTI -> error "randomValue: impossible"
EQI -> (ZR, g)
GTI -> gcastWith (unsafeCoerceRefl :: (1 <=? n) :~: True) $
let (v, g1) = randomValue range g
(rest, g2) = randomValue @(ListR (n - 1) a) range g1
in (v ::: rest, g2)
-- * Tuple instances
instance ( BaseTensor target
, AdaptableTarget target a
, AdaptableTarget target b )
=> AdaptableTarget target (a, b) where
type X (a, b) = TKProduct (X a) (X b)
toTarget (a, b) =
let a1 = toTarget a
b1 = toTarget b
in tpair a1 b1
fromTarget ab =
let a = fromTarget $ tproject1 ab
b = fromTarget $ tproject2 ab
in (a, b)
{-# SPECIALIZE instance (AdaptableTarget (AstTensor AstMethodLet FullSpan) a, AdaptableTarget (AstTensor AstMethodLet FullSpan) b) => AdaptableTarget (AstTensor AstMethodLet FullSpan) (a, b) #-}
instance (TermValue a, TermValue b) => TermValue (a, b) where
type Value (a, b) = (Value a, Value b)
fromValue (va, vb) = (fromValue va, fromValue vb)
instance (DualNumberValue a, DualNumberValue b) => DualNumberValue (a, b) where
type DValue (a, b) = (DValue a, DValue b)
fromDValue (va, vb) = (fromDValue va, fromDValue vb)
instance ( ForgetShape a
, ForgetShape b ) => ForgetShape (a, b) where
type NoShape (a, b) = (NoShape a, NoShape b)
forgetShape (a, b) = (forgetShape a, forgetShape b)
instance ( RandomValue a
, RandomValue b ) => RandomValue (a, b) where
randomValue range g =
let (v1, g1) = randomValue range g
(v2, g2) = randomValue range g1
in ((v1, v2), g2)
instance ( BaseTensor target
, AdaptableTarget target a
, AdaptableTarget target b
, AdaptableTarget target c )
=> AdaptableTarget target (a, b, c) where
type X (a, b, c) = TKProduct (TKProduct (X a) (X b)) (X c)
toTarget (a, b, c) =
let a1 = toTarget a
b1 = toTarget b
c1 = toTarget c
in tpair (tpair a1 b1) c1
fromTarget abc =
let a = fromTarget $ tproject1 $ tproject1 abc
b = fromTarget $ tproject2 $ tproject1 abc
c = fromTarget $ tproject2 abc
in (a, b, c)
{-# SPECIALIZE instance (AdaptableTarget (AstTensor AstMethodLet FullSpan) a, AdaptableTarget (AstTensor AstMethodLet FullSpan) b, AdaptableTarget (AstTensor AstMethodLet FullSpan) c) => AdaptableTarget (AstTensor AstMethodLet FullSpan) (a, b, c) #-}
instance (TermValue a, TermValue b, TermValue c)
=> TermValue (a, b, c) where
type Value (a, b, c) = (Value a, Value b, Value c)
fromValue (va, vb, vc) = (fromValue va, fromValue vb, fromValue vc)
instance (DualNumberValue a, DualNumberValue b, DualNumberValue c)
=> DualNumberValue (a, b, c) where
type DValue (a, b, c) = (DValue a, DValue b, DValue c)
fromDValue (va, vb, vc) = (fromDValue va, fromDValue vb, fromDValue vc)
instance ( ForgetShape a
, ForgetShape b
, ForgetShape c ) => ForgetShape (a, b, c) where
type NoShape (a, b, c) = (NoShape a, NoShape b, NoShape c)
forgetShape (a, b, c) = (forgetShape a, forgetShape b, forgetShape c)
instance ( RandomValue a
, RandomValue b
, RandomValue c ) => RandomValue (a, b, c) where
randomValue range g =
let (v1, g1) = randomValue range g
(v2, g2) = randomValue range g1
(v3, g3) = randomValue range g2
in ((v1, v2, v3), g3)
instance ( BaseTensor target
, AdaptableTarget target a
, AdaptableTarget target b
, AdaptableTarget target c
, AdaptableTarget target d)
=> AdaptableTarget target (a, b, c, d) where
type X (a, b, c, d) = TKProduct (TKProduct (X a) (X b))
(TKProduct (X c) (X d))
toTarget (a, b, c, d) =
let a1 = toTarget a
b1 = toTarget b
c1 = toTarget c
d1 = toTarget d
in tpair (tpair a1 b1) (tpair c1 d1)
fromTarget abcd =
let a = fromTarget $ tproject1 $ tproject1 abcd
b = fromTarget $ tproject2 $ tproject1 abcd
c = fromTarget $ tproject1 $ tproject2 abcd
d = fromTarget $ tproject2 $ tproject2 abcd
in (a, b, c, d)
{-# SPECIALIZE instance (AdaptableTarget (AstTensor AstMethodLet FullSpan) a, AdaptableTarget (AstTensor AstMethodLet FullSpan) b, AdaptableTarget (AstTensor AstMethodLet FullSpan) c, AdaptableTarget (AstTensor AstMethodLet FullSpan) d) => AdaptableTarget (AstTensor AstMethodLet FullSpan) (a, b, c, d) #-}
instance (TermValue a, TermValue b, TermValue c, TermValue d)
=> TermValue (a, b, c, d) where
type Value (a, b, c, d) = (Value a, Value b, Value c, Value d)
fromValue (va, vb, vc, vd) =
(fromValue va, fromValue vb, fromValue vc, fromValue vd)
instance ( DualNumberValue a, DualNumberValue b, DualNumberValue c
, DualNumberValue d )
=> DualNumberValue (a, b, c, d) where
type DValue (a, b, c, d) = (DValue a, DValue b, DValue c, DValue d)
fromDValue (va, vb, vc, vd) =
(fromDValue va, fromDValue vb, fromDValue vc, fromDValue vd)
instance ( ForgetShape a
, ForgetShape b
, ForgetShape c
, ForgetShape d ) => ForgetShape (a, b, c, d) where
type NoShape (a, b, c, d) =
(NoShape a, NoShape b, NoShape c, NoShape d)
forgetShape (a, b, c, d) =
(forgetShape a, forgetShape b, forgetShape c, forgetShape d)
instance ( RandomValue a
, RandomValue b
, RandomValue c
, RandomValue d ) => RandomValue (a, b, c, d) where
randomValue range g =
let (v1, g1) = randomValue range g
(v2, g2) = randomValue range g1
(v3, g3) = randomValue range g2
(v4, g4) = randomValue range g3
in ((v1, v2, v3, v4), g4)
instance ( BaseTensor target
, AdaptableTarget target a
, AdaptableTarget target b
, AdaptableTarget target c
, AdaptableTarget target d
, AdaptableTarget target e)
=> AdaptableTarget target (a, b, c, d, e) where
type X (a, b, c, d, e) = TKProduct (TKProduct (TKProduct (X a) (X b)) (X c))
(TKProduct (X d) (X e))
toTarget (a, b, c, d, e) =
let a1 = toTarget a
b1 = toTarget b
c1 = toTarget c
d1 = toTarget d
e1 = toTarget e
in tpair (tpair (tpair a1 b1) c1) (tpair d1 e1)
fromTarget abcde =
let a = fromTarget $ tproject1 $ tproject1 $ tproject1 abcde
b = fromTarget $ tproject2 $ tproject1 $ tproject1 abcde
c = fromTarget $ tproject2 $ tproject1 abcde
d = fromTarget $ tproject1 $ tproject2 abcde
e = fromTarget $ tproject2 $ tproject2 abcde
in (a, b, c, d, e)
{-# SPECIALIZE instance (AdaptableTarget (AstTensor AstMethodLet FullSpan) a, AdaptableTarget (AstTensor AstMethodLet FullSpan) b, AdaptableTarget (AstTensor AstMethodLet FullSpan) c, AdaptableTarget (AstTensor AstMethodLet FullSpan) d, AdaptableTarget (AstTensor AstMethodLet FullSpan) e) => AdaptableTarget (AstTensor AstMethodLet FullSpan) (a, b, c, d, e) #-}
instance (TermValue a, TermValue b, TermValue c, TermValue d, TermValue e)
=> TermValue (a, b, c, d, e) where
type Value (a, b, c, d, e) = (Value a, Value b, Value c, Value d, Value e)
fromValue (va, vb, vc, vd, ve) =
(fromValue va, fromValue vb, fromValue vc, fromValue vd, fromValue ve)
instance ( DualNumberValue a, DualNumberValue b, DualNumberValue c
, DualNumberValue d, DualNumberValue e )
=> DualNumberValue (a, b, c, d, e) where
type DValue (a, b, c, d, e) =
(DValue a, DValue b, DValue c, DValue d, DValue e)
fromDValue (va, vb, vc, vd, ve) =
(fromDValue va, fromDValue vb, fromDValue vc, fromDValue vd, fromDValue ve)
instance ( ForgetShape a
, ForgetShape b
, ForgetShape c
, ForgetShape d
, ForgetShape e ) => ForgetShape (a, b, c, d, e) where
type NoShape (a, b, c, d, e) =
(NoShape a, NoShape b, NoShape c, NoShape d, NoShape e)
forgetShape (a, b, c, d, e) =
(forgetShape a, forgetShape b, forgetShape c, forgetShape d, forgetShape e)
instance ( RandomValue a
, RandomValue b
, RandomValue c
, RandomValue d
, RandomValue e ) => RandomValue (a, b, c, d, e) where
randomValue range g =
let (v1, g1) = randomValue range g
(v2, g2) = randomValue range g1
(v3, g3) = randomValue range g2
(v4, g4) = randomValue range g3
(v5, g5) = randomValue range g4
in ((v1, v2, v3, v4, v5), g5)