packages feed

ad 3.2.2 → 3.3.0.1

raw patch · 14 files changed

+737/−736 lines, 14 filesdep ~freePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: free

API changes (from Hackage documentation)

- Numeric.AD.Internal.Reverse: Var :: !a -> {-# UNPACK #-} !Int -> Tape a t
- Numeric.AD.Internal.Reverse: class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o
- Numeric.AD.Internal.Reverse: data Tape a t
- Numeric.AD.Internal.Reverse: derivative :: Num a => AD Reverse a -> a
- Numeric.AD.Internal.Reverse: derivative' :: Num a => AD Reverse a -> (a, a)
- Numeric.AD.Internal.Reverse: instance (Data a, Data t) => Data (Tape a t)
- Numeric.AD.Internal.Reverse: instance (Show a, Show t) => Show (Tape a t)
- Numeric.AD.Internal.Reverse: instance Grad i o o' a => Grad (AD Reverse a -> i) (a -> o) (a -> o') a
- Numeric.AD.Internal.Reverse: instance Lifted Reverse
- Numeric.AD.Internal.Reverse: instance Lifted Reverse => Jacobian Reverse
- Numeric.AD.Internal.Reverse: instance Lifted Reverse => Mode Reverse
- Numeric.AD.Internal.Reverse: instance Monad S
- Numeric.AD.Internal.Reverse: instance MuRef (Reverse a)
- Numeric.AD.Internal.Reverse: instance Num a => Grad (AD Reverse a) [a] (a, [a]) a
- Numeric.AD.Internal.Reverse: instance Primal Reverse
- Numeric.AD.Internal.Reverse: instance Show a => Show (Reverse a)
- Numeric.AD.Internal.Reverse: instance Typeable1 Reverse
- Numeric.AD.Internal.Reverse: instance Typeable2 Tape
- Numeric.AD.Internal.Reverse: instance Var Reverse
- Numeric.AD.Internal.Reverse: newtype Reverse a
- Numeric.AD.Internal.Reverse: pack :: Grad i o o' a => i -> [AD Reverse a] -> AD Reverse a
- Numeric.AD.Internal.Reverse: partialArray :: Num a => (Int, Int) -> AD Reverse a -> Array Int a
- Numeric.AD.Internal.Reverse: partialMap :: Num a => AD Reverse a -> IntMap a
- Numeric.AD.Internal.Reverse: unpack :: Grad i o o' a => ([a] -> [a]) -> o
- Numeric.AD.Internal.Reverse: unpack' :: Grad i o o' a => ([a] -> (a, [a])) -> o'
- Numeric.AD.Internal.Reverse: vgrad :: Grad i o o' a => i -> o
- Numeric.AD.Internal.Reverse: vgrad' :: Grad i o o' a => i -> o'
- Numeric.AD.Internal.Wengert: Binary :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> a -> a -> Cells -> Cells
- Numeric.AD.Internal.Wengert: Head :: {-# UNPACK #-} !Int -> Cells -> Head
- Numeric.AD.Internal.Wengert: Lift :: a -> Wengert s a
- Numeric.AD.Internal.Wengert: Nil :: Cells
- Numeric.AD.Internal.Wengert: Tape :: IORef Head -> Tape
- Numeric.AD.Internal.Wengert: Unary :: {-# UNPACK #-} !Int -> a -> Cells -> Cells
- Numeric.AD.Internal.Wengert: Wengert :: {-# UNPACK #-} !Int -> a -> Wengert s a
- Numeric.AD.Internal.Wengert: Zero :: Wengert s a
- Numeric.AD.Internal.Wengert: data Cells
- Numeric.AD.Internal.Wengert: data Head
- Numeric.AD.Internal.Wengert: data Wengert s a
- Numeric.AD.Internal.Wengert: derivativeOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Wengert s) a -> a
- Numeric.AD.Internal.Wengert: derivativeOf' :: (Reifies s Tape, Num a) => Proxy s -> AD (Wengert s) a -> (a, a)
- Numeric.AD.Internal.Wengert: getTape :: Tape -> IORef Head
- Numeric.AD.Internal.Wengert: instance (Reifies s Tape, Lifted (Wengert s)) => Jacobian (Wengert s)
- Numeric.AD.Internal.Wengert: instance (Reifies s Tape, Lifted (Wengert s)) => Mode (Wengert s)
- Numeric.AD.Internal.Wengert: instance Primal (Wengert s)
- Numeric.AD.Internal.Wengert: instance Reifies s Tape => Lifted (Wengert s)
- Numeric.AD.Internal.Wengert: instance Show a => Show (Wengert s a)
- Numeric.AD.Internal.Wengert: instance Typeable2 Wengert
- Numeric.AD.Internal.Wengert: instance Var (Wengert s)
- Numeric.AD.Internal.Wengert: newtype Tape
- Numeric.AD.Internal.Wengert: partialArrayOf :: (Reifies s Tape, Num a) => Proxy s -> (Int, Int) -> AD (Wengert s) a -> Array Int a
- Numeric.AD.Internal.Wengert: partialMapOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Wengert s) a -> IntMap a
- Numeric.AD.Internal.Wengert: partials :: (Reifies s Tape, Num a) => AD (Wengert s) a -> [a]
- Numeric.AD.Internal.Wengert: reifyTape :: Int -> (forall s. Reifies s Tape => Proxy s -> r) -> r
- Numeric.AD.Mode.Directed: Wengert :: Direction
- Numeric.AD.Mode.Reverse: class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o
- Numeric.AD.Mode.Reverse: vgrad :: Grad i o o' a => i -> o
- Numeric.AD.Mode.Reverse: vgrad' :: Grad i o o' a => i -> o'
- Numeric.AD.Mode.Wengert: diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a
- Numeric.AD.Mode.Wengert: diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)
- Numeric.AD.Mode.Wengert: diffF :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f a
- Numeric.AD.Mode.Wengert: diffF' :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f (a, a)
- Numeric.AD.Mode.Wengert: grad :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a
- Numeric.AD.Mode.Wengert: grad' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f a)
- Numeric.AD.Mode.Wengert: gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f b
- Numeric.AD.Mode.Wengert: gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f b)
- Numeric.AD.Mode.Wengert: hessian :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f (f a)
- Numeric.AD.Mode.Wengert: hessianF :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f (f a))
- Numeric.AD.Mode.Wengert: jacobian :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a)
- Numeric.AD.Mode.Wengert: jacobian' :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f a)
- Numeric.AD.Mode.Wengert: jacobianWith :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f b)
- Numeric.AD.Mode.Wengert: jacobianWith' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f b)
- Numeric.AD.Variadic.Reverse: class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o
- Numeric.AD.Variadic.Reverse: vgrad :: Grad i o o' a => i -> o
- Numeric.AD.Variadic.Reverse: vgrad' :: Grad i o o' a => i -> o'
+ Numeric.AD.Internal.Kahn: Binary :: !a -> a -> a -> t -> t -> Tape a t
+ Numeric.AD.Internal.Kahn: Kahn :: (Tape a (Kahn a)) -> Kahn a
+ Numeric.AD.Internal.Kahn: Lift :: !a -> Tape a t
+ Numeric.AD.Internal.Kahn: Unary :: !a -> a -> t -> Tape a t
+ Numeric.AD.Internal.Kahn: Var :: !a -> {-# UNPACK #-} !Int -> Tape a t
+ Numeric.AD.Internal.Kahn: Zero :: Tape a t
+ Numeric.AD.Internal.Kahn: class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o
+ Numeric.AD.Internal.Kahn: data Tape a t
+ Numeric.AD.Internal.Kahn: derivative :: Num a => AD Kahn a -> a
+ Numeric.AD.Internal.Kahn: derivative' :: Num a => AD Kahn a -> (a, a)
+ Numeric.AD.Internal.Kahn: instance (Data a, Data t) => Data (Tape a t)
+ Numeric.AD.Internal.Kahn: instance (Show a, Show t) => Show (Tape a t)
+ Numeric.AD.Internal.Kahn: instance Grad i o o' a => Grad (AD Kahn a -> i) (a -> o) (a -> o') a
+ Numeric.AD.Internal.Kahn: instance Lifted Kahn
+ Numeric.AD.Internal.Kahn: instance Lifted Kahn => Jacobian Kahn
+ Numeric.AD.Internal.Kahn: instance Lifted Kahn => Mode Kahn
+ Numeric.AD.Internal.Kahn: instance Monad S
+ Numeric.AD.Internal.Kahn: instance MuRef (Kahn a)
+ Numeric.AD.Internal.Kahn: instance Num a => Grad (AD Kahn a) [a] (a, [a]) a
+ Numeric.AD.Internal.Kahn: instance Primal Kahn
+ Numeric.AD.Internal.Kahn: instance Show a => Show (Kahn a)
+ Numeric.AD.Internal.Kahn: instance Typeable1 Kahn
+ Numeric.AD.Internal.Kahn: instance Typeable2 Tape
+ Numeric.AD.Internal.Kahn: instance Var Kahn
+ Numeric.AD.Internal.Kahn: newtype Kahn a
+ Numeric.AD.Internal.Kahn: pack :: Grad i o o' a => i -> [AD Kahn a] -> AD Kahn a
+ Numeric.AD.Internal.Kahn: partialArray :: Num a => (Int, Int) -> AD Kahn a -> Array Int a
+ Numeric.AD.Internal.Kahn: partialMap :: Num a => AD Kahn a -> IntMap a
+ Numeric.AD.Internal.Kahn: partials :: Num a => AD Kahn a -> [(Int, a)]
+ Numeric.AD.Internal.Kahn: unpack :: Grad i o o' a => ([a] -> [a]) -> o
+ Numeric.AD.Internal.Kahn: unpack' :: Grad i o o' a => ([a] -> (a, [a])) -> o'
+ Numeric.AD.Internal.Kahn: vgrad :: Grad i o o' a => i -> o
+ Numeric.AD.Internal.Kahn: vgrad' :: Grad i o o' a => i -> o'
+ Numeric.AD.Internal.Reverse: Head :: {-# UNPACK #-} !Int -> Cells -> Head
+ Numeric.AD.Internal.Reverse: Nil :: Cells
+ Numeric.AD.Internal.Reverse: Tape :: IORef Head -> Tape
+ Numeric.AD.Internal.Reverse: data Cells
+ Numeric.AD.Internal.Reverse: data Head
+ Numeric.AD.Internal.Reverse: data Reverse s a
+ Numeric.AD.Internal.Reverse: derivativeOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Reverse s) a -> a
+ Numeric.AD.Internal.Reverse: derivativeOf' :: (Reifies s Tape, Num a) => Proxy s -> AD (Reverse s) a -> (a, a)
+ Numeric.AD.Internal.Reverse: getTape :: Tape -> IORef Head
+ Numeric.AD.Internal.Reverse: instance (Reifies s Tape, Lifted (Reverse s)) => Jacobian (Reverse s)
+ Numeric.AD.Internal.Reverse: instance (Reifies s Tape, Lifted (Reverse s)) => Mode (Reverse s)
+ Numeric.AD.Internal.Reverse: instance Primal (Reverse s)
+ Numeric.AD.Internal.Reverse: instance Reifies s Tape => Lifted (Reverse s)
+ Numeric.AD.Internal.Reverse: instance Show a => Show (Reverse s a)
+ Numeric.AD.Internal.Reverse: instance Typeable2 Reverse
+ Numeric.AD.Internal.Reverse: instance Var (Reverse s)
+ Numeric.AD.Internal.Reverse: newtype Tape
+ Numeric.AD.Internal.Reverse: partialArrayOf :: (Reifies s Tape, Num a) => Proxy s -> (Int, Int) -> AD (Reverse s) a -> Array Int a
+ Numeric.AD.Internal.Reverse: partialMapOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Reverse s) a -> IntMap a
+ Numeric.AD.Internal.Reverse: reifyTape :: Int -> (forall s. Reifies s Tape => Proxy s -> r) -> r
+ Numeric.AD.Mode.Directed: Kahn :: Direction
+ Numeric.AD.Mode.Kahn: class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o
+ Numeric.AD.Mode.Kahn: diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a
+ Numeric.AD.Mode.Kahn: diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)
+ Numeric.AD.Mode.Kahn: diffF :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f a
+ Numeric.AD.Mode.Kahn: diffF' :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f (a, a)
+ Numeric.AD.Mode.Kahn: grad :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a
+ Numeric.AD.Mode.Kahn: grad' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f a)
+ Numeric.AD.Mode.Kahn: gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f b
+ Numeric.AD.Mode.Kahn: gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f b)
+ Numeric.AD.Mode.Kahn: hessian :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f (f a)
+ Numeric.AD.Mode.Kahn: hessianF :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f (f a))
+ Numeric.AD.Mode.Kahn: jacobian :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a)
+ Numeric.AD.Mode.Kahn: jacobian' :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f a)
+ Numeric.AD.Mode.Kahn: jacobianWith :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f b)
+ Numeric.AD.Mode.Kahn: jacobianWith' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f b)
+ Numeric.AD.Mode.Kahn: vgrad :: Grad i o o' a => i -> o
+ Numeric.AD.Mode.Kahn: vgrad' :: Grad i o o' a => i -> o'
+ Numeric.AD.Variadic.Kahn: class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o
+ Numeric.AD.Variadic.Kahn: vgrad :: Grad i o o' a => i -> o
+ Numeric.AD.Variadic.Kahn: vgrad' :: Grad i o o' a => i -> o'
- Numeric.AD.Internal.Reverse: Binary :: !a -> a -> a -> t -> t -> Tape a t
+ Numeric.AD.Internal.Reverse: Binary :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> a -> a -> Cells -> Cells
- Numeric.AD.Internal.Reverse: Lift :: !a -> Tape a t
+ Numeric.AD.Internal.Reverse: Lift :: a -> Reverse s a
- Numeric.AD.Internal.Reverse: Reverse :: (Tape a (Reverse a)) -> Reverse a
+ Numeric.AD.Internal.Reverse: Reverse :: {-# UNPACK #-} !Int -> a -> Reverse s a
- Numeric.AD.Internal.Reverse: Unary :: !a -> a -> t -> Tape a t
+ Numeric.AD.Internal.Reverse: Unary :: {-# UNPACK #-} !Int -> a -> Cells -> Cells
- Numeric.AD.Internal.Reverse: Zero :: Tape a t
+ Numeric.AD.Internal.Reverse: Zero :: Reverse s a
- Numeric.AD.Internal.Reverse: partials :: Num a => AD Reverse a -> [(Int, a)]
+ Numeric.AD.Internal.Reverse: partials :: (Reifies s Tape, Num a) => AD (Reverse s) a -> [a]

Files

CHANGELOG.markdown view
@@ -1,3 +1,9 @@+3.3+---+* Renamed `Reverse` to `Kahn` and `Wengert` to `Reverse`. We use Arthur Kahn's topological sorting algorithm to+  sort the tape after the fact in Kahn mode, while the stock Reverse mode builds a Wengert list as it goes, which+  is more efficient in practice.+ 3.2.2 ----- * Export of the `conjugateGradientDescent` and `gradientDescent` from `Numeric.AD`
README.markdown view
@@ -87,7 +87,7 @@  * `Numeric.AD.Mode.Forward` provides basic forward-mode AD. It is good for computing simple derivatives.  * `Numeric.AD.Mode.Sparse` computes a sparse forward-mode AD tower. It is good for higher derivatives or large numbers of outputs.  * `Numeric.AD.Mode.Reverse` computes with reverse-mode AD. It is good for computing a few outputs given many inputs.- * `Numeric.AD.Mode.Chain` computes with reverse-mode AD. It is good for computing a few outputs given many inputs, when not using sparks.+ * `Numeric.AD.Mode.Wengert` computes with reverse-mode AD. It is good for computing a few outputs given many inputs, when not using sparks.  * `Numeric.AD.Mode.Tower` computes a dense forward-mode AD tower useful for higher derivatives of single input functions.   * `Numeric.AD.Newton` provides a number of combinators for root finding using Newton's method with quadratic convergence.
ad.cabal view
@@ -1,5 +1,5 @@ name:         ad-version:      3.2.2+version:      3.3.0.1 license:      BSD3 license-File: LICENSE copyright:    (c) Edward Kmett 2010-2012,@@ -24,9 +24,9 @@     .     * @Numeric.AD.Mode.Forward@ provides basic forward-mode AD. It is good for computing simple derivatives.     .-    * @Numeric.AD.Mode.Reverse@ uses benign side-effects to compute reverse-mode AD. It is good for computing gradients in one pass. It generates a tree-like tape that needs to be topologically sorted in the end.+    * @Numeric.AD.Mode.Reverse@ uses benign side-effects to compute reverse-mode AD. It is good for computing gradients in one pass. It generates a Wengert list (linear tape) using @Data.Reflection@.     .-    * @Numeric.AD.Mode.Wengert@ uses benign side-effects to compute reverse-mode AD. It is good for computing gradients in one pass. It generates a Wengert list (linear tape) using @Data.Reflection@.+    * @Numeric.AD.Mode.Kahn@ uses benign side-effects to compute reverse-mode AD. It is good for computing gradients in one pass. It generates a tree-like tape that needs to be topologically sorted in the end.     .     * @Numeric.AD.Mode.Sparse@ computes a sparse forward-mode AD tower. It is good for higher derivatives or large numbers of outputs.     .@@ -91,7 +91,7 @@     comonad          == 3.0.*,     containers       >= 0.2 && < 0.6,     data-reify       >= 0.6 && < 0.7,-    free             >= 3.0 && <= 3.3,+    free             >= 3.0 && <= 3.4,     mtl,     reflection       >= 1.1.6 && < 1.2,     tagged           >= 0.4.2.1 && < 0.5,@@ -104,24 +104,24 @@     Numeric.AD.Newton     Numeric.AD.Halley -    Numeric.AD.Mode.Wengert     Numeric.AD.Mode.Directed     Numeric.AD.Mode.Forward+    Numeric.AD.Mode.Kahn     Numeric.AD.Mode.Reverse     Numeric.AD.Mode.Tower     Numeric.AD.Mode.Sparse      Numeric.AD.Variadic-    Numeric.AD.Variadic.Reverse+    Numeric.AD.Variadic.Kahn     Numeric.AD.Variadic.Sparse      Numeric.AD.Internal.Classes     Numeric.AD.Internal.Combinators     Numeric.AD.Internal.Forward     Numeric.AD.Internal.Tower+    Numeric.AD.Internal.Kahn     Numeric.AD.Internal.Reverse     Numeric.AD.Internal.Var-    Numeric.AD.Internal.Wengert     Numeric.AD.Internal.Sparse     Numeric.AD.Internal.Dense     Numeric.AD.Internal.Composition
+ src/Numeric/AD/Internal/Kahn.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE Rank2Types, TypeFamilies, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TemplateHaskell, UndecidableInstances, DeriveDataTypeable #-}+-- {-# OPTIONS_HADDOCK hide, prune #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Numeric.AD.Internal.Kahn+-- Copyright   :  (c) Edward Kmett 2010+-- License     :  BSD3+-- Maintainer  :  ekmett@gmail.com+-- Stability   :  experimental+-- Portability :  GHC only+--+-- This module provides reverse-mode Automatic Differentiation implementation using+-- linear time topological sorting after the fact.+--+-- For this form of reverse-mode AD we use 'System.Mem.StableName.StableName' to recover+-- sharing information from the tape to avoid combinatorial explosion, and thus+-- run asymptotically faster than it could without such sharing information, but the use+-- of side-effects contained herein is benign.+--+-----------------------------------------------------------------------------++module Numeric.AD.Internal.Kahn+    ( Kahn(..)+    , Tape(..)+    , partials+    , partialArray+    , partialMap+    , derivative+    , derivative'+    , vgrad, vgrad'+    , Grad(..)+    ) where++import Prelude hiding (mapM)+import Control.Applicative (Applicative(..),(<$>))+import Control.Monad.ST+import Control.Monad (forM_)+import Data.List (foldl')+import Data.Array.ST+import Data.Array+import Data.IntMap (IntMap, fromListWith)+import Data.Graph (Vertex, transposeG, Graph)+import Data.Reify (reifyGraph, MuRef(..))+import qualified Data.Reify.Graph as Reified+import System.IO.Unsafe (unsafePerformIO)+import Language.Haskell.TH+import Data.Data (Data)+import Data.Typeable (Typeable)+import Numeric.AD.Internal.Types+import Numeric.AD.Internal.Classes+import Numeric.AD.Internal.Identity+import Numeric.AD.Internal.Var++-- | A @Tape@ records the information needed back propagate from the output to each input during reverse 'Mode' AD.+data Tape a t+    = Zero+    | Lift !a+    | Var !a {-# UNPACK #-} !Int+    | Binary !a a a t t+    | Unary !a a t+    deriving (Show, Data, Typeable)++-- | @Kahn@ is a 'Mode' using reverse-mode automatic differentiation that provides fast 'diffFU', 'diff2FU', 'grad', 'grad2' and a fast 'jacobian' when you have a significantly smaller number of outputs than inputs.+newtype Kahn a = Kahn (Tape a (Kahn a)) deriving (Show, Typeable)++-- deriving instance (Data (Tape a (Kahn a)) => Data (Kahn a)++instance MuRef (Kahn a) where+    type DeRef (Kahn a) = Tape a++    mapDeRef _ (Kahn Zero) = pure Zero+    mapDeRef _ (Kahn (Lift a)) = pure (Lift a)+    mapDeRef _ (Kahn (Var a v)) = pure (Var a v)+    mapDeRef f (Kahn (Binary a dadb dadc b c)) = Binary a dadb dadc <$> f b <*> f c+    mapDeRef f (Kahn (Unary a dadb b)) = Unary a dadb <$> f b++instance Lifted Kahn => Mode Kahn where+    isKnownZero (Kahn Zero) = True+    isKnownZero _    = False++    isKnownConstant (Kahn Zero) = True+    isKnownConstant (Kahn (Lift _)) = True+    isKnownConstant _ = False++    auto a = Kahn (Lift a)+    zero   = Kahn Zero+    (<+>)  = binary (+) one one+    a *^ b = lift1 (a *) (\_ -> auto a) b+    a ^* b = lift1 (* b) (\_ -> auto b) a+    a ^/ b = lift1 (/ b) (\_ -> auto (recip b)) a++    Kahn Zero <**> y                = auto (0 ** primal y)+    _            <**> Kahn Zero     = auto 1+    x            <**> Kahn (Lift y) = lift1 (**y) (\z -> (y *^ z ** Id (y-1))) x+    x            <**> y                = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y++instance Primal Kahn where+    primal (Kahn Zero) = 0+    primal (Kahn (Lift a)) = a+    primal (Kahn (Var a _)) = a+    primal (Kahn (Binary a _ _ _ _)) = a+    primal (Kahn (Unary a _ _)) = a++instance Lifted Kahn => Jacobian Kahn where+    type D Kahn = Id++    unary f _         (Kahn Zero)     = Kahn (Lift (f 0))+    unary f _         (Kahn (Lift a)) = Kahn (Lift (f a))+    unary f (Id dadb) b                  = Kahn (Unary (f (primal b)) dadb b)++    lift1 f df b = unary f (df (Id pb)) b+        where pb = primal b++    lift1_ f df b = unary (const a) (df (Id a) (Id pb)) b+        where pb = primal b+              a = f pb++    binary f _         _         (Kahn Zero)     (Kahn Zero)     = Kahn (Lift (f 0 0))+    binary f _         _         (Kahn Zero)     (Kahn (Lift c)) = Kahn (Lift (f 0 c))+    binary f _         _         (Kahn (Lift b)) (Kahn Zero)     = Kahn (Lift (f b 0))+    binary f _         _         (Kahn (Lift b)) (Kahn (Lift c)) = Kahn (Lift (f b c))+    binary f _         (Id dadc) (Kahn Zero)     c                  = Kahn (Unary (f 0 (primal c)) dadc c)+    binary f _         (Id dadc) (Kahn (Lift b)) c                  = Kahn (Unary (f b (primal c)) dadc c)+    binary f (Id dadb) _         b                  (Kahn Zero)     = Kahn (Unary (f (primal b) 0) dadb b)+    binary f (Id dadb) _         b                  (Kahn (Lift c)) = Kahn (Unary (f (primal b) c) dadb b)+    binary f (Id dadb) (Id dadc) b                  c                  = Kahn (Binary (f (primal b) (primal c)) dadb dadc b c)++    lift2 f df b c = binary f dadb dadc b c+        where (dadb, dadc) = df (Id (primal b)) (Id (primal c))++    lift2_ f df b c = binary (\_ _ -> a) dadb dadc b c+        where+            pb = primal b+            pc = primal c+            a = f pb pc+            (dadb, dadc) = df (Id a) (Id pb) (Id pc)++deriveLifted id (conT ''Kahn)++derivative :: Num a => AD Kahn a -> a+derivative = sum . map snd . partials+{-# INLINE derivative #-}++derivative' :: Num a => AD Kahn a -> (a, a)+derivative' r = (primal r, derivative r)+{-# INLINE derivative' #-}++-- | back propagate sensitivities along a tape.+backPropagate :: Num a => (Vertex -> (Tape a Int, Int, [Int])) -> STArray s Int a -> Vertex -> ST s ()+backPropagate vmap ss v = do+        case node of+            Unary _ g b -> do+                da <- readArray ss i+                db <- readArray ss b+                writeArray ss b (db + g*da)+            Binary _ gb gc b c -> do+                da <- readArray ss i+                db <- readArray ss b+                writeArray ss b (db + gb*da)+                dc <- readArray ss c+                writeArray ss c (dc + gc*da)+            _ -> return ()+    where+        (node, i, _) = vmap v+        -- this isn't _quite_ right, as it should allow negative zeros to multiply through++topSortAcyclic :: Graph -> [Vertex]+topSortAcyclic g = reverse $ runST $ do+    del <- newArray (bounds g) False :: ST s (STUArray s Int Bool)+    let tg = transposeG g+        starters = [ n | (n, []) <- assocs tg ]+        loop [] rs = return rs+        loop (n:ns) rs = do+            writeArray del n True+            let add [] = return ns+                add (m:ms) = do+                    b <- ok (tg!m)+                    ms' <- add ms+                    if b then return (m:ms') else return ms'+                ok [] = return True+                ok (x:xs) = do b <- readArray del x; if b then ok xs else return False+            ns' <- add (g!n)+            loop ns' (n : rs)+    loop starters []++-- | This returns a list of contributions to the partials.+-- The variable ids returned in the list are likely /not/ unique!+{-# SPECIALIZE partials :: AD Kahn Double -> [(Int, Double)] #-}+partials :: forall a . Num a => AD Kahn a -> [(Int, a)]+partials (AD tape) = [ let v = sensitivities ! ix in seq v (ident, v) | (ix, Var _ ident) <- xs ]+    where+        Reified.Graph xs start = unsafePerformIO $ reifyGraph tape+        g = array xsBounds [ (i, successors t) | (i, t) <- xs ]+        vertexMap = array xsBounds xs+        vmap i = (vertexMap ! i, i, [])+        xsBounds = sbounds xs++        sensitivities = runSTArray $ do+            ss <- newArray xsBounds 0+            writeArray ss start 1+            forM_ (topSortAcyclic g) $+                backPropagate vmap ss+            return ss++        sbounds ((a,_):as) = foldl' (\(lo,hi) (b,_) -> let lo' = min lo b; hi' = max hi b in lo' `seq` hi' `seq` (lo', hi')) (a,a) as+        sbounds _ = undefined -- the graph can't be empty, it contains the output node!++        successors :: Tape a t -> [t]+        successors (Unary _ _ b) = [b]+        successors (Binary _ _ _ b c) = [b,c]+        successors _ = []++-- | Return an 'Array' of 'partials' given bounds for the variable IDs.+partialArray :: Num a => (Int, Int) -> AD Kahn a -> Array Int a+partialArray vbounds tape = accumArray (+) 0 vbounds (partials tape)+{-# INLINE partialArray #-}++-- | Return an 'IntMap' of sparse partials+partialMap :: Num a => AD Kahn a -> IntMap a+partialMap = fromListWith (+) . partials+{-# INLINE partialMap #-}++-- A simple fresh variable supply monad+newtype S a = S { runS :: Int -> (a,Int) }+instance Monad S where+    return a = S (\s -> (a,s))+    S g >>= f = S (\s -> let (a,s') = g s in runS (f a) s')++instance Var Kahn where+    var a v = Kahn (Var a v)+    varId (Kahn (Var _ v)) = v+    varId _ = error "varId: not a Var"++class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o where+    pack :: i -> [AD Kahn a] -> AD Kahn a+    unpack :: ([a] -> [a]) -> o+    unpack' :: ([a] -> (a, [a])) -> o'++instance Num a => Grad (AD Kahn a) [a] (a, [a]) a where+    pack i _ = i+    unpack f = f []+    unpack' f = f []++instance Grad i o o' a => Grad (AD Kahn a -> i) (a -> o) (a -> o') a where+    pack f (a:as) = pack (f a) as+    pack _ [] = error "Grad.pack: logic error"+    unpack f a = unpack (f . (a:))+    unpack' f a = unpack' (f . (a:))++vgrad :: Grad i o o' a => i -> o+vgrad i = unpack (unsafeGrad (pack i))+    where+        unsafeGrad f as = unbind vs (partialArray bds $ f vs)+            where+                (vs,bds) = bind as++vgrad' :: Grad i o o' a => i -> o'+vgrad' i = unpack' (unsafeGrad' (pack i))+    where+        unsafeGrad' f as = (primal r, unbind vs (partialArray bds r))+            where+                r = f vs+                (vs,bds) = bind as+
src/Numeric/AD/Internal/Reverse.hs view
@@ -1,112 +1,137 @@-{-# LANGUAGE Rank2Types, TypeFamilies, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TemplateHaskell, UndecidableInstances, DeriveDataTypeable #-}+{-# LANGUAGE Rank2Types, TypeFamilies, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TemplateHaskell, UndecidableInstances, DeriveDataTypeable, GADTs, ScopedTypeVariables #-} -- {-# OPTIONS_HADDOCK hide, prune #-} ----------------------------------------------------------------------------- -- | -- Module      :  Numeric.AD.Internal.Reverse--- Copyright   :  (c) Edward Kmett 2010+-- Copyright   :  (c) Edward Kmett 2012 -- License     :  BSD3 -- Maintainer  :  ekmett@gmail.com -- Stability   :  experimental -- Portability :  GHC only ----- Reverse-Mode Automatic Differentiation implementation details+-- Reverse-Mode Automatic Differentiation using a single Wengert list (or \"tape\"). ----- For reverse mode AD we use 'System.Mem.StableName.StableName' to recover sharing information from--- the tape to avoid combinatorial explosion, and thus run asymptotically faster--- than it could without such sharing information, but the use of side-effects--- contained herein is benign.+-- This version uses @Data.Reflection@ to find and update the tape. --+-- This is asymptotically faster than using @Reverse@, which+-- is forced to reify and topologically sort the graph, but it requires+-- a fairly expensive rendezvous during construction when updated using+-- multiple threads.+-- -----------------------------------------------------------------------------  module Numeric.AD.Internal.Reverse     ( Reverse(..)     , Tape(..)+    , Head(..)+    , Cells(..)+    , reifyTape     , partials-    , partialArray-    , partialMap-    , derivative-    , derivative'-    , vgrad, vgrad'-    , Grad(..)+    , partialArrayOf+    , partialMapOf+    , derivativeOf+    , derivativeOf'     ) where -import Prelude hiding (mapM)-import Control.Applicative (Applicative(..),(<$>)) import Control.Monad.ST-import Control.Monad (forM_)-import Data.List (foldl') import Data.Array.ST import Data.Array-import Data.IntMap (IntMap, fromListWith)-import Data.Graph (Vertex, transposeG, Graph)-import Data.Reify (reifyGraph, MuRef(..))-import qualified Data.Reify.Graph as Reified-import System.IO.Unsafe (unsafePerformIO)-import Language.Haskell.TH-import Data.Data (Data)-import Data.Typeable (Typeable)+import Data.Array.Unsafe as Unsafe+import Data.IORef+import Data.IntMap (IntMap, fromDistinctAscList)+import Data.Proxy+import Data.Reflection+import Data.Typeable+import Language.Haskell.TH hiding (reify) import Numeric.AD.Internal.Types import Numeric.AD.Internal.Classes import Numeric.AD.Internal.Identity import Numeric.AD.Internal.Var+import Prelude hiding (mapM)+import System.IO.Unsafe (unsafePerformIO)+import Unsafe.Coerce --- | A @Tape@ records the information needed back propagate from the output to each input during 'Reverse' 'Mode' AD.-data Tape a t-    = Zero-    | Lift !a-    | Var !a {-# UNPACK #-} !Int-    | Binary !a a a t t-    | Unary !a a t-    deriving (Show, Data, Typeable)+-- evil untyped tape+data Cells where+  Nil    :: Cells+  Unary  :: {-# UNPACK #-} !Int -> a -> Cells -> Cells+  Binary :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> a -> a -> Cells -> Cells --- | @Reverse@ is a 'Mode' using reverse-mode automatic differentiation that provides fast 'diffFU', 'diff2FU', 'grad', 'grad2' and a fast 'jacobian' when you have a significantly smaller number of outputs than inputs.-newtype Reverse a = Reverse (Tape a (Reverse a)) deriving (Show, Typeable)+dropCells :: Int -> Cells -> Cells+dropCells 0 xs = xs+dropCells _ Nil = Nil+dropCells n (Unary _ _ xs)      = (dropCells $! n - 1) xs+dropCells n (Binary _ _ _ _ xs) = (dropCells $! n - 1) xs --- deriving instance (Data (Tape a (Reverse a)) => Data (Reverse a)+data Head = Head {-# UNPACK #-} !Int Cells -instance MuRef (Reverse a) where-    type DeRef (Reverse a) = Tape a+newtype Tape = Tape { getTape :: IORef Head } -    mapDeRef _ (Reverse Zero) = pure Zero-    mapDeRef _ (Reverse (Lift a)) = pure (Lift a)-    mapDeRef _ (Reverse (Var a v)) = pure (Var a v)-    mapDeRef f (Reverse (Binary a dadb dadc b c)) = Binary a dadb dadc <$> f b <*> f c-    mapDeRef f (Reverse (Unary a dadb b)) = Unary a dadb <$> f b+un :: Int -> a -> Head -> (Head, Int)+un i di (Head r t) = h `seq` r' `seq` (h, r') where+  r' = r + 1+  h = Head r' (Unary i di t)+{-# INLINE un #-} -instance Lifted Reverse => Mode Reverse where-    isKnownZero (Reverse Zero) = True-    isKnownZero _    = False+bin :: Int -> Int -> a -> a -> Head -> (Head, Int)+bin i j di dj (Head r t) = h `seq` r' `seq` (h, r') where+  r' = r + 1+  h = Head r' (Binary i j di dj t)+{-# INLINE bin #-} -    isKnownConstant (Reverse Zero) = True-    isKnownConstant (Reverse (Lift _)) = True-    isKnownConstant _ = False+modifyTape :: Reifies s Tape => p s -> (Head -> (Head, r)) -> IO r+modifyTape p = atomicModifyIORef (getTape (reflect p))+{-# INLINE modifyTape #-} -    auto a = Reverse (Lift a)-    zero   = Reverse Zero-    (<+>)  = binary (+) one one-    a *^ b = lift1 (a *) (\_ -> auto a) b-    a ^* b = lift1 (* b) (\_ -> auto b) a-    a ^/ b = lift1 (/ b) (\_ -> auto (recip b)) a+-- | This is used to create a new entry on the chain given a unary function, its derivative with respect to its input,+-- the variable ID of its input, and the value of its input. Used by 'unary' and 'binary' internally.+unarily :: forall s a. Reifies s Tape => (a -> a) -> a -> Int -> a -> Reverse s a+unarily f di i b = Reverse (unsafePerformIO (modifyTape (Proxy :: Proxy s) (un i di))) $! f b+{-# INLINE unarily #-} -    Reverse Zero <**> y                = auto (0 ** primal y)-    _            <**> Reverse Zero     = auto 1-    x            <**> Reverse (Lift y) = lift1 (**y) (\z -> (y *^ z ** Id (y-1))) x-    x            <**> y                = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y+-- | This is used to create a new entry on the chain given a binary function, its derivatives with respect to its inputs,+-- their variable IDs and values. Used by 'binary' internally.+binarily :: forall s a. Reifies s Tape => (a -> a -> a) -> a -> a -> Int -> a -> Int -> a -> Reverse s a+binarily f di dj i b j c = Reverse (unsafePerformIO (modifyTape (Proxy :: Proxy s) (bin i j di dj))) $! f b c+{-# INLINE binarily #-} -instance Primal Reverse where-    primal (Reverse Zero) = 0-    primal (Reverse (Lift a)) = a-    primal (Reverse (Var a _)) = a-    primal (Reverse (Binary a _ _ _ _)) = a-    primal (Reverse (Unary a _ _)) = a+data Reverse s a where+  Zero :: Reverse s a+  Lift :: a -> Reverse s a+  Reverse :: {-# UNPACK #-} !Int -> a -> Reverse s a+  deriving (Show, Typeable) -instance Lifted Reverse => Jacobian Reverse where-    type D Reverse = Id+instance (Reifies s Tape, Lifted (Reverse s)) => Mode (Reverse s) where+  isKnownZero Zero = True+  isKnownZero _    = False -    unary f _         (Reverse Zero)     = Reverse (Lift (f 0))-    unary f _         (Reverse (Lift a)) = Reverse (Lift (f a))-    unary f (Id dadb) b                  = Reverse (Unary (f (primal b)) dadb b)+  isKnownConstant Reverse{} = False+  isKnownConstant _ = True +  auto = Lift+  zero = Zero+  (<+>)  = binary (+) one one+  a *^ b = lift1 (a *) (\_ -> auto a) b+  a ^* b = lift1 (* b) (\_ -> auto b) a+  a ^/ b = lift1 (/ b) (\_ -> auto (recip b)) a++  Zero <**> y      = auto (0 ** primal y)+  _    <**> Zero   = auto 1+  x    <**> Lift y = lift1 (**y) (\z -> (y *^ z ** Id (y-1))) x+  x    <**> y      = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y++instance Primal (Reverse s) where+    primal Zero = 0+    primal (Lift a) = a+    primal (Reverse _ a) = a++instance (Reifies s Tape, Lifted (Reverse s)) => Jacobian (Reverse s) where+    type D (Reverse s) = Id++    unary f _         (Zero)   = Lift (f 0)+    unary f _         (Lift a) = Lift (f a)+    unary f (Id dadi) (Reverse i b) = unarily f dadi i b+     lift1 f df b = unary f (df (Id pb)) b         where pb = primal b @@ -114,16 +139,17 @@         where pb = primal b               a = f pb -    binary f _         _         (Reverse Zero)     (Reverse Zero)     = Reverse (Lift (f 0 0))-    binary f _         _         (Reverse Zero)     (Reverse (Lift c)) = Reverse (Lift (f 0 c))-    binary f _         _         (Reverse (Lift b)) (Reverse Zero)     = Reverse (Lift (f b 0))-    binary f _         _         (Reverse (Lift b)) (Reverse (Lift c)) = Reverse (Lift (f b c))-    binary f _         (Id dadc) (Reverse Zero)     c                  = Reverse (Unary (f 0 (primal c)) dadc c)-    binary f _         (Id dadc) (Reverse (Lift b)) c                  = Reverse (Unary (f b (primal c)) dadc c)-    binary f (Id dadb) _         b                  (Reverse Zero)     = Reverse (Unary (f (primal b) 0) dadb b)-    binary f (Id dadb) _         b                  (Reverse (Lift c)) = Reverse (Unary (f (primal b) c) dadb b)-    binary f (Id dadb) (Id dadc) b                  c                  = Reverse (Binary (f (primal b) (primal c)) dadb dadc b c)+    binary f _         _         Zero     Zero     = Lift (f 0 0)+    binary f _         _         Zero     (Lift c) = Lift (f 0 c)+    binary f _         _         (Lift b) Zero     = Lift (f b 0)+    binary f _         _         (Lift b) (Lift c) = Lift (f b c) +    binary f _         (Id dadc) Zero        (Reverse i c) = unarily (f 0) dadc i c+    binary f _         (Id dadc) (Lift b)    (Reverse i c) = unarily (f b) dadc i c+    binary f (Id dadb) _         (Reverse i b) Zero        = unarily (`f` 0) dadb i b+    binary f (Id dadb) _         (Reverse i b) (Lift c)    = unarily (`f` c) dadb i b+    binary f (Id dadb) (Id dadc) (Reverse i b) (Reverse j c) = binarily f dadb dadc i b j c+     lift2 f df b c = binary f dadb dadc b c         where (dadb, dadc) = df (Id (primal b)) (Id (primal c)) @@ -134,130 +160,68 @@             a = f pb pc             (dadb, dadc) = df (Id a) (Id pb) (Id pc) -deriveLifted id (conT ''Reverse)--derivative :: Num a => AD Reverse a -> a-derivative = sum . map snd . partials-{-# INLINE derivative #-}--derivative' :: Num a => AD Reverse a -> (a, a)-derivative' r = (primal r, derivative r)-{-# INLINE derivative' #-}---- | back propagate sensitivities along a tape.-backPropagate :: Num a => (Vertex -> (Tape a Int, Int, [Int])) -> STArray s Int a -> Vertex -> ST s ()-backPropagate vmap ss v = do-        case node of-            Unary _ g b -> do-                da <- readArray ss i-                db <- readArray ss b-                writeArray ss b (db + g*da)-            Binary _ gb gc b c -> do-                da <- readArray ss i-                db <- readArray ss b-                writeArray ss b (db + gb*da)-                dc <- readArray ss c-                writeArray ss c (dc + gc*da)-            _ -> return ()-    where-        (node, i, _) = vmap v-        -- this isn't _quite_ right, as it should allow negative zeros to multiply through--topSortAcyclic :: Graph -> [Vertex]-topSortAcyclic g = reverse $ runST $ do-    del <- newArray (bounds g) False :: ST s (STUArray s Int Bool)-    let tg = transposeG g-        starters = [ n | (n, []) <- assocs tg ]-        loop [] rs = return rs-        loop (n:ns) rs = do-            writeArray del n True-            let add [] = return ns-                add (m:ms) = do-                    b <- ok (tg!m)-                    ms' <- add ms-                    if b then return (m:ms') else return ms'-                ok [] = return True-                ok (x:xs) = do b <- readArray del x; if b then ok xs else return False-            ns' <- add (g!n)-            loop ns' (n : rs)-    loop starters []+let s = varT (mkName "s") in+  deriveLifted (classP ''Reifies [s, conT ''Tape] :) (conT ''Reverse `appT` s) --- | This returns a list of contributions to the partials.--- The variable ids returned in the list are likely /not/ unique!-{-# SPECIALIZE partials :: AD Reverse Double -> [(Int, Double)] #-}-partials :: forall a . Num a => AD Reverse a -> [(Int, a)]-partials (AD tape) = [ let v = sensitivities ! ix in seq v (ident, v) | (ix, Var _ ident) <- xs ]-    where-        Reified.Graph xs start = unsafePerformIO $ reifyGraph tape-        g = array xsBounds [ (i, successors t) | (i, t) <- xs ]-        vertexMap = array xsBounds xs-        vmap i = (vertexMap ! i, i, [])-        xsBounds = sbounds xs+-- | Helper that extracts the derivative of a chain when the chain was constructed with one variable.+derivativeOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Reverse s) a -> a+derivativeOf _ = sum . partials+{-# INLINE derivativeOf #-} -        sensitivities = runSTArray $ do-            ss <- newArray xsBounds 0-            writeArray ss start 1-            forM_ (topSortAcyclic g) $-                backPropagate vmap ss-            return ss+-- | Helper that extracts both the primal and derivative of a chain when the chain was constructed with one variable.+derivativeOf' :: (Reifies s Tape, Num a) => Proxy s -> AD (Reverse s) a -> (a, a)+derivativeOf' p r = (primal r, derivativeOf p r)+{-# INLINE derivativeOf' #-} -        sbounds ((a,_):as) = foldl' (\(lo,hi) (b,_) -> let lo' = min lo b; hi' = max hi b in lo' `seq` hi' `seq` (lo', hi')) (a,a) as-        sbounds _ = undefined -- the graph can't be empty, it contains the output node!+-- | Used internally to push sensitivities down the chain.+backPropagate :: Num a => Int -> Cells -> STArray s Int a -> ST s Int+backPropagate k Nil _ = return k+backPropagate k (Unary i g xs) ss = do+  da <- readArray ss k+  db <- readArray ss i+  writeArray ss i $! db + unsafeCoerce g*da+  (backPropagate $! k - 1) xs ss+backPropagate k (Binary i j g h xs) ss = do+  da <- readArray ss k+  db <- readArray ss i+  writeArray ss i $! db + unsafeCoerce g*da+  dc <- readArray ss j+  writeArray ss j $! dc + unsafeCoerce h*da+  (backPropagate $! k - 1) xs ss -        successors :: Tape a t -> [t]-        successors (Unary _ _ b) = [b]-        successors (Binary _ _ _ b c) = [b,c]-        successors _ = []+-- | Extract the partials from the current chain for a given AD variable.+{-# SPECIALIZE partials :: Reifies s Tape => AD (Reverse s) Double -> [Double] #-}+partials :: forall s a. (Reifies s Tape, Num a) => AD (Reverse s) a -> [a]+partials (AD Zero)        = []+partials (AD (Lift _))    = []+partials (AD (Reverse k _)) = map (sensitivities !) [0..vs] where+   Head n t = unsafePerformIO $ readIORef (getTape (reflect (Proxy :: Proxy s)))+   tk = dropCells (n - k) t+   (vs,sensitivities) = runST $ do+     ss <- newArray (0, k) 0+     writeArray ss k 1+     v <- backPropagate k tk ss+     as <- Unsafe.unsafeFreeze ss+     return (v, as)  -- | Return an 'Array' of 'partials' given bounds for the variable IDs.-partialArray :: Num a => (Int, Int) -> AD Reverse a -> Array Int a-partialArray vbounds tape = accumArray (+) 0 vbounds (partials tape)-{-# INLINE partialArray #-}+partialArrayOf :: (Reifies s Tape, Num a) => Proxy s -> (Int, Int) -> AD (Reverse s) a -> Array Int a+partialArrayOf _ vbounds = accumArray (+) 0 vbounds . zip [0..] . partials+{-# INLINE partialArrayOf #-}  -- | Return an 'IntMap' of sparse partials-partialMap :: Num a => AD Reverse a -> IntMap a-partialMap = fromListWith (+) . partials-{-# INLINE partialMap #-}+partialMapOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Reverse s) a -> IntMap a+partialMapOf _ = fromDistinctAscList . zip [0..] . partials+{-# INLINE partialMapOf #-} --- A simple fresh variable supply monad-newtype S a = S { runS :: Int -> (a,Int) }-instance Monad S where-    return a = S (\s -> (a,s))-    S g >>= f = S (\s -> let (a,s') = g s in runS (f a) s')+-- | Construct a tape that starts with @n@ variables.+reifyTape :: Int -> (forall s. Reifies s Tape => Proxy s -> r) -> r+reifyTape vs k = unsafePerformIO $ do+  h <- newIORef (Head vs Nil)+  return (reify (Tape h) k)+{-# NOINLINE reifyTape #-} -instance Var Reverse where-    var a v = Reverse (Var a v)-    varId (Reverse (Var _ v)) = v+instance Var (Reverse s) where+    var a v = Reverse v a+    varId (Reverse v _) = v     varId _ = error "varId: not a Var"--class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o where-    pack :: i -> [AD Reverse a] -> AD Reverse a-    unpack :: ([a] -> [a]) -> o-    unpack' :: ([a] -> (a, [a])) -> o'--instance Num a => Grad (AD Reverse a) [a] (a, [a]) a where-    pack i _ = i-    unpack f = f []-    unpack' f = f []--instance Grad i o o' a => Grad (AD Reverse a -> i) (a -> o) (a -> o') a where-    pack f (a:as) = pack (f a) as-    pack _ [] = error "Grad.pack: logic error"-    unpack f a = unpack (f . (a:))-    unpack' f a = unpack' (f . (a:))--vgrad :: Grad i o o' a => i -> o-vgrad i = unpack (unsafeGrad (pack i))-    where-        unsafeGrad f as = unbind vs (partialArray bds $ f vs)-            where-                (vs,bds) = bind as--vgrad' :: Grad i o o' a => i -> o'-vgrad' i = unpack' (unsafeGrad' (pack i))-    where-        unsafeGrad' f as = (primal r, unbind vs (partialArray bds r))-            where-                r = f vs-                (vs,bds) = bind as-
src/Numeric/AD/Internal/Var.hs view
@@ -8,13 +8,7 @@ -- Stability   :  experimental -- Portability :  GHC only ----- Reverse-Mode Automatic Differentiation using a single tape.------ This version uses @Data.Reflection@ to update a single tape.------ This is asymptotically faster than using @Reverse@, which--- is forced to reify and topologically sort the graph, but it is--- less friendly to the use of sparks.+-- Variables used for reverse-mode automatic differentiation. -----------------------------------------------------------------------------  module Numeric.AD.Internal.Var
− src/Numeric/AD/Internal/Wengert.hs
@@ -1,227 +0,0 @@-{-# LANGUAGE Rank2Types, TypeFamilies, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, TemplateHaskell, UndecidableInstances, DeriveDataTypeable, GADTs, ScopedTypeVariables #-}--- {-# OPTIONS_HADDOCK hide, prune #-}--------------------------------------------------------------------------------- |--- Module      :  Numeric.AD.Internal.Wengert--- Copyright   :  (c) Edward Kmett 2012--- License     :  BSD3--- Maintainer  :  ekmett@gmail.com--- Stability   :  experimental--- Portability :  GHC only------ Reverse-Mode Automatic Differentiation using a single Wengert list (or \"tape\").------ This version uses @Data.Reflection@ to find and update the tape.------ This is asymptotically faster than using @Reverse@, which--- is forced to reify and topologically sort the graph, but it requires--- a fairly expensive rendezvous during construction when updated using--- multiple threads.-----------------------------------------------------------------------------------module Numeric.AD.Internal.Wengert-    ( Wengert(..)-    , Tape(..)-    , Head(..)-    , Cells(..)-    , reifyTape-    , partials-    , partialArrayOf-    , partialMapOf-    , derivativeOf-    , derivativeOf'-    ) where--import Control.Monad.ST-import Data.Array.ST-import Data.Array-import Data.Array.Unsafe as Unsafe-import Data.IORef-import Data.IntMap (IntMap, fromDistinctAscList)-import Data.Proxy-import Data.Reflection-import Data.Typeable-import Language.Haskell.TH hiding (reify)-import Numeric.AD.Internal.Types-import Numeric.AD.Internal.Classes-import Numeric.AD.Internal.Identity-import Numeric.AD.Internal.Var-import Prelude hiding (mapM)-import System.IO.Unsafe (unsafePerformIO)-import Unsafe.Coerce---- evil untyped tape-data Cells where-  Nil    :: Cells-  Unary  :: {-# UNPACK #-} !Int -> a -> Cells -> Cells-  Binary :: {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> a -> a -> Cells -> Cells--dropCells :: Int -> Cells -> Cells-dropCells 0 xs = xs-dropCells _ Nil = Nil-dropCells n (Unary _ _ xs)      = (dropCells $! n - 1) xs-dropCells n (Binary _ _ _ _ xs) = (dropCells $! n - 1) xs--data Head = Head {-# UNPACK #-} !Int Cells--newtype Tape = Tape { getTape :: IORef Head }--un :: Int -> a -> Head -> (Head, Int)-un i di (Head r t) = h `seq` r' `seq` (h, r') where-  r' = r + 1-  h = Head r' (Unary i di t)-{-# INLINE un #-}--bin :: Int -> Int -> a -> a -> Head -> (Head, Int)-bin i j di dj (Head r t) = h `seq` r' `seq` (h, r') where-  r' = r + 1-  h = Head r' (Binary i j di dj t)-{-# INLINE bin #-}--modifyTape :: Reifies s Tape => p s -> (Head -> (Head, r)) -> IO r-modifyTape p = atomicModifyIORef (getTape (reflect p))-{-# INLINE modifyTape #-}---- | This is used to create a new entry on the chain given a unary function, its derivative with respect to its input,--- the variable ID of its input, and the value of its input. Used by 'unary' and 'binary' internally.-unarily :: forall s a. Reifies s Tape => (a -> a) -> a -> Int -> a -> Wengert s a-unarily f di i b = Wengert (unsafePerformIO (modifyTape (Proxy :: Proxy s) (un i di))) $! f b-{-# INLINE unarily #-}---- | This is used to create a new entry on the chain given a binary function, its derivatives with respect to its inputs,--- their variable IDs and values. Used by 'binary' internally.-binarily :: forall s a. Reifies s Tape => (a -> a -> a) -> a -> a -> Int -> a -> Int -> a -> Wengert s a-binarily f di dj i b j c = Wengert (unsafePerformIO (modifyTape (Proxy :: Proxy s) (bin i j di dj))) $! f b c-{-# INLINE binarily #-}--data Wengert s a where-  Zero :: Wengert s a-  Lift :: a -> Wengert s a-  Wengert :: {-# UNPACK #-} !Int -> a -> Wengert s a-  deriving (Show, Typeable)--instance (Reifies s Tape, Lifted (Wengert s)) => Mode (Wengert s) where-  isKnownZero Zero = True-  isKnownZero _    = False--  isKnownConstant Wengert{} = False-  isKnownConstant _ = True--  auto = Lift-  zero = Zero-  (<+>)  = binary (+) one one-  a *^ b = lift1 (a *) (\_ -> auto a) b-  a ^* b = lift1 (* b) (\_ -> auto b) a-  a ^/ b = lift1 (/ b) (\_ -> auto (recip b)) a--  Zero <**> y      = auto (0 ** primal y)-  _    <**> Zero   = auto 1-  x    <**> Lift y = lift1 (**y) (\z -> (y *^ z ** Id (y-1))) x-  x    <**> y      = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y--instance Primal (Wengert s) where-    primal Zero = 0-    primal (Lift a) = a-    primal (Wengert _ a) = a--instance (Reifies s Tape, Lifted (Wengert s)) => Jacobian (Wengert s) where-    type D (Wengert s) = Id--    unary f _         (Zero)   = Lift (f 0)-    unary f _         (Lift a) = Lift (f a)-    unary f (Id dadi) (Wengert i b) = unarily f dadi i b--    lift1 f df b = unary f (df (Id pb)) b-        where pb = primal b--    lift1_ f df b = unary (const a) (df (Id a) (Id pb)) b-        where pb = primal b-              a = f pb--    binary f _         _         Zero     Zero     = Lift (f 0 0)-    binary f _         _         Zero     (Lift c) = Lift (f 0 c)-    binary f _         _         (Lift b) Zero     = Lift (f b 0)-    binary f _         _         (Lift b) (Lift c) = Lift (f b c)--    binary f _         (Id dadc) Zero        (Wengert i c) = unarily (f 0) dadc i c-    binary f _         (Id dadc) (Lift b)    (Wengert i c) = unarily (f b) dadc i c-    binary f (Id dadb) _         (Wengert i b) Zero        = unarily (`f` 0) dadb i b-    binary f (Id dadb) _         (Wengert i b) (Lift c)    = unarily (`f` c) dadb i b-    binary f (Id dadb) (Id dadc) (Wengert i b) (Wengert j c) = binarily f dadb dadc i b j c--    lift2 f df b c = binary f dadb dadc b c-        where (dadb, dadc) = df (Id (primal b)) (Id (primal c))--    lift2_ f df b c = binary (\_ _ -> a) dadb dadc b c-        where-            pb = primal b-            pc = primal c-            a = f pb pc-            (dadb, dadc) = df (Id a) (Id pb) (Id pc)--let s = varT (mkName "s") in-  deriveLifted (classP ''Reifies [s, conT ''Tape] :) (conT ''Wengert `appT` s)---- | Helper that extracts the derivative of a chain when the chain was constructed with one variable.-derivativeOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Wengert s) a -> a-derivativeOf _ = sum . partials-{-# INLINE derivativeOf #-}---- | Helper that extracts both the primal and derivative of a chain when the chain was constructed with one variable.-derivativeOf' :: (Reifies s Tape, Num a) => Proxy s -> AD (Wengert s) a -> (a, a)-derivativeOf' p r = (primal r, derivativeOf p r)-{-# INLINE derivativeOf' #-}---- | Used internally to push sensitivities down the chain.-backPropagate :: Num a => Int -> Cells -> STArray s Int a -> ST s Int-backPropagate k Nil _ = return k-backPropagate k (Unary i g xs) ss = do-  da <- readArray ss k-  db <- readArray ss i-  writeArray ss i $! db + unsafeCoerce g*da-  (backPropagate $! k - 1) xs ss-backPropagate k (Binary i j g h xs) ss = do-  da <- readArray ss k-  db <- readArray ss i-  writeArray ss i $! db + unsafeCoerce g*da-  dc <- readArray ss j-  writeArray ss j $! dc + unsafeCoerce h*da-  (backPropagate $! k - 1) xs ss---- | Extract the partials from the current chain for a given AD variable.-{-# SPECIALIZE partials :: Reifies s Tape => AD (Wengert s) Double -> [Double] #-}-partials :: forall s a. (Reifies s Tape, Num a) => AD (Wengert s) a -> [a]-partials (AD Zero)        = []-partials (AD (Lift _))    = []-partials (AD (Wengert k _)) = map (sensitivities !) [0..vs] where-   Head n t = unsafePerformIO $ readIORef (getTape (reflect (Proxy :: Proxy s)))-   tk = dropCells (n - k) t-   (vs,sensitivities) = runST $ do-     ss <- newArray (0, k) 0-     writeArray ss k 1-     v <- backPropagate k tk ss-     as <- Unsafe.unsafeFreeze ss-     return (v, as)---- | Return an 'Array' of 'partials' given bounds for the variable IDs.-partialArrayOf :: (Reifies s Tape, Num a) => Proxy s -> (Int, Int) -> AD (Wengert s) a -> Array Int a-partialArrayOf _ vbounds = accumArray (+) 0 vbounds . zip [0..] . partials-{-# INLINE partialArrayOf #-}---- | Return an 'IntMap' of sparse partials-partialMapOf :: (Reifies s Tape, Num a) => Proxy s -> AD (Wengert s) a -> IntMap a-partialMapOf _ = fromDistinctAscList . zip [0..] . partials-{-# INLINE partialMapOf #-}---- | Construct a tape that starts with @n@ variables.-reifyTape :: Int -> (forall s. Reifies s Tape => Proxy s -> r) -> r-reifyTape vs k = unsafePerformIO $ do-  h <- newIORef (Head vs Nil)-  return (reify (Tape h) k)-{-# NOINLINE reifyTape #-}--instance Var (Wengert s) where-    var a v = Wengert v a-    varId (Wengert v _) = v-    varId _ = error "varId: not a Var"
src/Numeric/AD/Mode/Directed.hs view
@@ -30,67 +30,65 @@ import Prelude hiding (reverse) import Numeric.AD.Types import Data.Traversable (Traversable)-import qualified Numeric.AD.Mode.Reverse as R+import qualified Numeric.AD.Mode.Kahn as K import qualified Numeric.AD.Mode.Forward as F import qualified Numeric.AD.Mode.Tower as T-import qualified Numeric.AD.Mode.Wengert as W+import qualified Numeric.AD.Mode.Reverse as R import qualified Numeric.AD as M import Data.Ix --- TODO: use a data types a la carte approach, so we can expose more methods here--- rather than just the intersection of all of the functionality data Direction     = Forward+    | Kahn     | Reverse-    | Wengert     | Tower     | Mixed     deriving (Show, Eq, Ord, Read, Bounded, Enum, Ix)  diff :: Num a => Direction -> (forall s. Mode s => AD s a -> AD s a) -> a -> a diff Forward = F.diff+diff Kahn    = K.diff diff Reverse = R.diff-diff Wengert = W.diff-diff Tower = T.diff-diff Mixed = F.diff+diff Tower   = T.diff+diff Mixed   = F.diff {-# INLINE diff #-}  diff' :: Num a => Direction -> (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a) diff' Forward = F.diff'+diff' Kahn = K.diff' diff' Reverse = R.diff'-diff' Wengert = W.diff' diff' Tower = T.diff' diff' Mixed = F.diff' {-# INLINE diff' #-}  jacobian :: (Traversable f, Traversable g, Num a) => Direction -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a) jacobian Forward = F.jacobian+jacobian Kahn    = K.jacobian jacobian Reverse = R.jacobian-jacobian Wengert = W.jacobian-jacobian Tower = F.jacobian -- error "jacobian Tower: unimplemented"-jacobian Mixed = M.jacobian+jacobian Tower   = F.jacobian -- error "jacobian Tower: unimplemented"+jacobian Mixed   = M.jacobian {-# INLINE jacobian #-}  jacobian' :: (Traversable f, Traversable g, Num a) => Direction -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f a) jacobian' Forward = F.jacobian'+jacobian' Kahn    = K.jacobian' jacobian' Reverse = R.jacobian'-jacobian' Wengert = W.jacobian'-jacobian' Tower = F.jacobian' -- error "jacobian' Tower: unimplemented"-jacobian' Mixed = M.jacobian'+jacobian' Tower   = F.jacobian' -- error "jacobian' Tower: unimplemented"+jacobian' Mixed   = M.jacobian' {-# INLINE jacobian' #-}  grad :: (Traversable f, Num a) => Direction -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a grad Forward = F.grad+grad Kahn    = K.grad grad Reverse = R.grad-grad Wengert   = W.grad grad Tower   = F.grad -- error "grad Tower: unimplemented" grad Mixed   = M.grad {-# INLINE grad #-}  grad' :: (Traversable f, Num a) => Direction -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f a) grad' Forward = F.grad'+grad' Kahn    = K.grad' grad' Reverse = R.grad'-grad' Wengert   = W.grad' grad' Tower   = F.grad' -- error "grad' Tower: unimplemented" grad' Mixed   = M.grad' {-# INLINE grad' #-}
+ src/Numeric/AD/Mode/Kahn.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE Rank2Types, TemplateHaskell, BangPatterns, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Numeric.AD.Mode.Kahn+-- Copyright   :  (c) Edward Kmett 2010+-- License     :  BSD3+-- Maintainer  :  ekmett@gmail.com+-- Stability   :  experimental+-- Portability :  GHC only+--+-- This module provides reverse-mode Automatic Differentiation using post-hoc linear time+-- topological sorting.+--+-- For reverse mode AD we use 'System.Mem.StableName.StableName' to recover sharing information from+-- the tape to avoid combinatorial explosion, and thus run asymptotically faster+-- than it could without such sharing information, but the use of side-effects+-- contained herein is benign.+--+-----------------------------------------------------------------------------++module Numeric.AD.Mode.Kahn+    (+    -- * Gradient+      grad+    , grad'+    , gradWith+    , gradWith'++    -- * Jacobian+    , jacobian+    , jacobian'+    , jacobianWith+    , jacobianWith'+    -- * Hessian+    , hessian+    , hessianF+    -- * Derivatives+    , diff+    , diff'+    , diffF+    , diffF'+    -- * Unsafe Variadic Gradient+    , vgrad, vgrad'+    , Grad+    ) where++import Control.Applicative ((<$>))+import Data.Traversable (Traversable)++import Numeric.AD.Types+import Numeric.AD.Internal.Classes+import Numeric.AD.Internal.Composition+import Numeric.AD.Internal.Kahn+import Numeric.AD.Internal.Var++-- | The 'grad' function calculates the gradient of a non-scalar-to-scalar function with reverse-mode AD in a single pass.+--+-- >>> grad (\[x,y,z] -> x*y+z) [1,2,3]+-- [2,1,1]++grad :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a+grad f as = unbind vs (partialArray bds $ f vs)+    where (vs,bds) = bind as+{-# INLINE grad #-}++-- | The 'grad'' function calculates the result and gradient of a non-scalar-to-scalar function with reverse-mode AD in a single pass.+--+-- >>> grad' (\[x,y,z] -> 4*x*exp y+cos z) [1,2,3]+-- (28.566231899122155,[29.5562243957226,29.5562243957226,-0.1411200080598672])+grad' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f a)+grad' f as = (primal r, unbind vs $ partialArray bds r)+    where (vs, bds) = bind as+          r = f vs+{-# INLINE grad' #-}++-- | @'grad' g f@ function calculates the gradient of a non-scalar-to-scalar function @f@ with reverse-mode AD in a single pass.+-- The gradient is combined element-wise with the argument using the function @g@.+--+-- @+-- 'grad' = 'gradWith' (\_ dx -> dx)+-- 'id' = 'gradWith' const+-- @+--+--+gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f b+gradWith g f as = unbindWith g vs (partialArray bds $ f vs)+    where (vs,bds) = bind as+{-# INLINE gradWith #-}++-- | @'grad'' g f@ calculates the result and gradient of a non-scalar-to-scalar function @f@ with reverse-mode AD in a single pass+-- the gradient is combined element-wise with the argument using the function @g@.+--+-- @'grad'' == 'gradWith'' (\_ dx -> dx)@+gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f b)+gradWith' g f as = (primal r, unbindWith g vs $ partialArray bds r)+    where (vs, bds) = bind as+          r = f vs+{-# INLINE gradWith' #-}++-- | The 'jacobian' function calculates the jacobian of a non-scalar-to-non-scalar function with reverse AD lazily in @m@ passes for @m@ outputs.+--+-- >>> jacobian (\[x,y] -> [y,x,x*y]) [2,1]+-- [[0,1],[1,0],[1,2]]+--+-- >>> jacobian (\[x,y] -> [exp y,cos x,x+y]) [1,2]+-- [[0.0,7.38905609893065],[-0.8414709848078965,0.0],[1.0,1.0]]+jacobian :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a)+jacobian f as = unbind vs . partialArray bds <$> f vs where+    (vs, bds) = bind as+{-# INLINE jacobian #-}++-- | The 'jacobian'' function calculates both the result and the Jacobian of a nonscalar-to-nonscalar function, using @m@ invocations of reverse AD,+-- where @m@ is the output dimensionality. Applying @fmap snd@ to the result will recover the result of 'jacobian'+-- | An alias for 'gradF''+--+-- ghci> jacobian' (\[x,y] -> [y,x,x*y]) [2,1]+-- [(1,[0,1]),(2,[1,0]),(2,[1,2])]+jacobian' :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f a)+jacobian' f as = row <$> f vs where+    (vs, bds) = bind as+    row a = (primal a, unbind vs (partialArray bds a))+{-# INLINE jacobian' #-}++-- | 'jacobianWith g f' calculates the Jacobian of a non-scalar-to-non-scalar function @f@ with reverse AD lazily in @m@ passes for @m@ outputs.+--+-- Instead of returning the Jacobian matrix, the elements of the matrix are combined with the input using the @g@.+--+-- @+-- 'jacobian' = 'jacobianWith' (\_ dx -> dx)+-- 'jacobianWith' 'const' = (\f x -> 'const' x '<$>' f x)+-- @+jacobianWith :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f b)+jacobianWith g f as = unbindWith g vs . partialArray bds <$> f vs where+    (vs, bds) = bind as+{-# INLINE jacobianWith #-}++-- | 'jacobianWith' g f' calculates both the result and the Jacobian of a nonscalar-to-nonscalar function @f@, using @m@ invocations of reverse AD,+-- where @m@ is the output dimensionality. Applying @fmap snd@ to the result will recover the result of 'jacobianWith'+--+-- Instead of returning the Jacobian matrix, the elements of the matrix are combined with the input using the @g@.+--+-- @'jacobian'' == 'jacobianWith'' (\_ dx -> dx)@+jacobianWith' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f b)+jacobianWith' g f as = row <$> f vs where+    (vs, bds) = bind as+    row a = (primal a, unbindWith g vs (partialArray bds a))+{-# INLINE jacobianWith' #-}++-- | Compute the derivative of a function.+--+-- >>> diff sin 0+-- 1.0+--+-- >>> cos 0+-- 1.0+diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a+diff f a = derivative $ f (var a 0)+{-# INLINE diff #-}++-- | The 'diff'' function calculates the value and derivative, as a+-- pair, of a scalar-to-scalar function.+--+--+-- >>> diff' sin 0+-- (0.0,1.0)+diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)+diff' f a = derivative' $ f (var a 0)+{-# INLINE diff' #-}++-- | Compute the derivatives of a function that returns a vector with regards to its single input.+--+-- >>> diffF (\a -> [sin a, cos a]) 0+-- [1.0,0.0]+diffF :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f a+diffF f a = derivative <$> f (var a 0)+{-# INLINE diffF #-}++-- | Compute the derivatives of a function that returns a vector with regards to its single input+-- as well as the primal answer.+--+-- >>> diffF' (\a -> [sin a, cos a]) 0+-- [(0.0,1.0),(1.0,0.0)]+diffF' :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f (a, a)+diffF' f a = derivative' <$> f (var a 0)+{-# INLINE diffF' #-}++-- | Compute the 'hessian' via the 'jacobian' of the gradient. gradient is computed in reverse mode and then the 'jacobian' is computed in reverse mode.+--+-- However, since the @'grad' f :: f a -> f a@ is square this is not as fast as using the forward-mode 'jacobian' of a reverse mode gradient provided by 'Numeric.AD.hessian'.+--+-- >>> hessian (\[x,y] -> x*y) [1,2]+-- [[0,1],[1,0]]+hessian :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f (f a)+hessian f = jacobian (grad (decomposeMode . f . fmap composeMode))++-- | Compute the order 3 Hessian tensor on a non-scalar-to-non-scalar function via the reverse-mode Jacobian of the reverse-mode Jacobian of the function.+--+-- Less efficient than 'Numeric.AD.Mode.Mixed.hessianF'.+--+-- >>> hessianF (\[x,y] -> [x*y,x+y,exp x*cos y]) [1,2]+-- [[[0.0,1.0],[1.0,0.0]],[[0.0,0.0],[0.0,0.0]],[[-1.1312043837568135,-2.4717266720048188],[-2.4717266720048188,1.1312043837568135]]]+hessianF :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f (f a))+hessianF f = decomposeFunctor . jacobian (ComposeFunctor . jacobian (fmap decomposeMode . f . fmap composeMode))+
src/Numeric/AD/Mode/Reverse.hs view
@@ -8,12 +8,8 @@ -- Stability   :  experimental -- Portability :  GHC only ----- Mixed-Mode Automatic Differentiation.------ For reverse mode AD we use 'System.Mem.StableName.StableName' to recover sharing information from--- the tape to avoid combinatorial explosion, and thus run asymptotically faster--- than it could without such sharing information, but the use of side-effects--- contained herein is benign.+-- Reverse-mode automatic differentiation using Wengert lists and+-- Data.Reflection -- ----------------------------------------------------------------------------- @@ -30,17 +26,16 @@     , jacobian'     , jacobianWith     , jacobianWith'+     -- * Hessian     , hessian     , hessianF+     -- * Derivatives     , diff     , diff'     , diffF     , diffF'-    -- * Unsafe Variadic Gradient-    , vgrad, vgrad'-    , Grad     ) where  import Control.Applicative ((<$>))@@ -52,72 +47,70 @@ import Numeric.AD.Internal.Reverse import Numeric.AD.Internal.Var --- | The 'grad' function calculates the gradient of a non-scalar-to-scalar function with 'Reverse' AD in a single pass.+-- | The 'grad' function calculates the gradient of a non-scalar-to-scalar function with reverse-mode AD in a single pass. --+-- -- >>> grad (\[x,y,z] -> x*y+z) [1,2,3] -- [2,1,1]- grad :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a-grad f as = unbind vs (partialArray bds $ f vs)-    where (vs,bds) = bind as+grad f as = reifyTape (snd bds) $ \p -> unbind vs $! partialArrayOf p bds $! f $ vary <$> vs+  where (vs, bds) = bind as {-# INLINE grad #-} --- | The 'grad'' function calculates the result and gradient of a non-scalar-to-scalar function with 'Reverse' AD in a single pass.+-- | The 'grad'' function calculates the result and gradient of a non-scalar-to-scalar function with reverse-mode AD in a single pass. ----- >>> grad' (\[x,y,z] -> 4*x*exp y+cos z) [1,2,3]--- (28.566231899122155,[29.5562243957226,29.5562243957226,-0.1411200080598672])+-- >>> grad' (\[x,y,z] -> x*y+z) [1,2,3]+-- (5,[2,1,1]) grad' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f a)-grad' f as = (primal r, unbind vs $ partialArray bds r)-    where (vs, bds) = bind as-          r = f vs+grad' f as = reifyTape (snd bds) $ \p ->+  let r = f (fmap vary vs) in (primal r, unbind vs $! partialArrayOf p bds $! r)+  where (vs, bds) = bind as {-# INLINE grad' #-}  -- | @'grad' g f@ function calculates the gradient of a non-scalar-to-scalar function @f@ with reverse-mode AD in a single pass. -- The gradient is combined element-wise with the argument using the function @g@. -- -- @--- 'grad' = 'gradWith' (\_ dx -> dx)--- 'id' = 'gradWith' const+-- 'grad' == 'gradWith' (\_ dx -> dx)+-- 'id' == 'gradWith' 'const' -- @------ gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f b-gradWith g f as = unbindWith g vs (partialArray bds $ f vs)-    where (vs,bds) = bind as+gradWith g f as = reifyTape (snd bds) $ \p -> unbindWith g vs $! partialArrayOf p bds $! f $ vary <$> vs+  where (vs,bds) = bind as {-# INLINE gradWith #-} --- | @'grad'' g f@ calculates the result and gradient of a non-scalar-to-scalar function @f@ with 'Reverse' AD in a single pass+-- | @'grad'' g f@ calculates the result and gradient of a non-scalar-to-scalar function @f@ with reverse-mode AD in a single pass -- the gradient is combined element-wise with the argument using the function @g@. ----- @'grad'' == 'gradWith'' (\_ dx -> dx)@+-- @+-- 'grad'' == 'gradWith'' (\_ dx -> dx)+-- @ gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f b)-gradWith' g f as = (primal r, unbindWith g vs $ partialArray bds r)+gradWith' g f as = reifyTape (snd bds) $ \p ->+   let r = f (fmap vary vs) in (primal r, unbindWith g vs $! partialArrayOf p bds $! r)     where (vs, bds) = bind as-          r = f vs {-# INLINE gradWith' #-}  -- | The 'jacobian' function calculates the jacobian of a non-scalar-to-non-scalar function with reverse AD lazily in @m@ passes for @m@ outputs. -- -- >>> jacobian (\[x,y] -> [y,x,x*y]) [2,1] -- [[0,1],[1,0],[1,2]]------ >>> jacobian (\[x,y] -> [exp y,cos x,x+y]) [1,2]--- [[0.0,7.38905609893065],[-0.8414709848078965,0.0],[1.0,1.0]] jacobian :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a)-jacobian f as = unbind vs . partialArray bds <$> f vs where-    (vs, bds) = bind as+jacobian f as = reifyTape (snd bds) $ \p -> unbind vs . partialArrayOf p bds <$> f (fmap vary vs)+  where (vs, bds) = bind as {-# INLINE jacobian #-}  -- | The 'jacobian'' function calculates both the result and the Jacobian of a nonscalar-to-nonscalar function, using @m@ invocations of reverse AD, -- where @m@ is the output dimensionality. Applying @fmap snd@ to the result will recover the result of 'jacobian' -- | An alias for 'gradF'' ----- ghci> jacobian' (\[x,y] -> [y,x,x*y]) [2,1]+-- >>> jacobian' (\[x,y] -> [y,x,x*y]) [2,1] -- [(1,[0,1]),(2,[1,0]),(2,[1,2])] jacobian' :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f a)-jacobian' f as = row <$> f vs where-    (vs, bds) = bind as-    row a = (primal a, unbind vs (partialArray bds a))+jacobian' f as = reifyTape (snd bds) $ \p ->+  let row a = (primal a, unbind vs $! partialArrayOf p bds $! a)+  in row <$> f (vary <$> vs)+  where (vs, bds) = bind as {-# INLINE jacobian' #-}  -- | 'jacobianWith g f' calculates the Jacobian of a non-scalar-to-non-scalar function @f@ with reverse AD lazily in @m@ passes for @m@ outputs.@@ -125,11 +118,11 @@ -- Instead of returning the Jacobian matrix, the elements of the matrix are combined with the input using the @g@. -- -- @--- 'jacobian' = 'jacobianWith' (\_ dx -> dx)--- 'jacobianWith' 'const' = (\f x -> 'const' x '<$>' f x)+-- 'jacobian' == 'jacobianWith' (\_ dx -> dx)+-- 'jacobianWith' 'const' == (\f x -> 'const' x '<$>' f x) -- @ jacobianWith :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f b)-jacobianWith g f as = unbindWith g vs . partialArray bds <$> f vs where+jacobianWith g f as = reifyTape (snd bds) $ \p -> unbindWith g vs . partialArrayOf p bds <$> f (fmap vary vs) where     (vs, bds) = bind as {-# INLINE jacobianWith #-} @@ -139,53 +132,53 @@ -- Instead of returning the Jacobian matrix, the elements of the matrix are combined with the input using the @g@. -- -- @'jacobian'' == 'jacobianWith'' (\_ dx -> dx)@+-- jacobianWith' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f b)-jacobianWith' g f as = row <$> f vs where-    (vs, bds) = bind as-    row a = (primal a, unbindWith g vs (partialArray bds a))+jacobianWith' g f as = reifyTape (snd bds) $ \p ->+  let row a = (primal a, unbindWith g vs $! partialArrayOf p bds $! a)+  in row <$> f (vary <$> vs)+  where (vs, bds) = bind as {-# INLINE jacobianWith' #-}  -- | Compute the derivative of a function. -- -- >>> diff sin 0 -- 1.0------ >>> cos 0--- 1.0 diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a-diff f a = derivative $ f (var a 0)+diff f a = reifyTape 1 $ \p -> derivativeOf p $! f (var a 0) {-# INLINE diff #-} --- | The 'diff'' function calculates the value and derivative, as a--- pair, of a scalar-to-scalar function.---+-- | The 'diff'' function calculates the result and derivative, as a pair, of a scalar-to-scalar function. -- -- >>> diff' sin 0 -- (0.0,1.0)+--+-- >>> diff' exp 0+-- (1.0,1.0) diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)-diff' f a = derivative' $ f (var a 0)+diff' f a = reifyTape 1 $ \p -> derivativeOf' p $! f (var a 0) {-# INLINE diff' #-} --- | Compute the derivatives of a function that returns a vector with regards to its single input.+-- | Compute the derivatives of each result of a scalar-to-vector function with regards to its input. -- -- >>> diffF (\a -> [sin a, cos a]) 0 -- [1.0,0.0]+-- diffF :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f a-diffF f a = derivative <$> f (var a 0)+diffF f a = reifyTape 1 $ \p -> derivativeOf p <$> f (var a 0) {-# INLINE diffF #-} --- | Compute the derivatives of a function that returns a vector with regards to its single input--- as well as the primal answer.+-- | Compute the derivatives of each result of a scalar-to-vector function with regards to its input along with the answer. -- -- >>> diffF' (\a -> [sin a, cos a]) 0 -- [(0.0,1.0),(1.0,0.0)] diffF' :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f (a, a)-diffF' f a = derivative' <$> f (var a 0)+diffF' f a = reifyTape 1 $ \p -> derivativeOf' p <$> f (var a 0) {-# INLINE diffF' #-} --- | Compute the 'hessian' via the 'jacobian' of the gradient. gradient is computed in reverse mode and then the 'jacobian' is computed in reverse mode.+-- | Compute the hessian via the jacobian of the gradient. gradient is computed in reverse mode and then the jacobian is computed in reverse mode. ----- However, since the @'grad' f :: f a -> f a@ is square this is not as fast as using the forward-mode 'jacobian' of a reverse mode gradient provided by 'Numeric.AD.hessian'.+-- However, since the @'grad' f :: f a -> f a@ is square this is not as fast as using the forward-mode Jacobian of a reverse mode gradient provided by 'Numeric.AD.hessian'. -- -- >>> hessian (\[x,y] -> x*y) [1,2] -- [[0,1],[1,0]]@@ -200,4 +193,3 @@ -- [[[0.0,1.0],[1.0,0.0]],[[0.0,0.0],[0.0,0.0]],[[-1.1312043837568135,-2.4717266720048188],[-2.4717266720048188,1.1312043837568135]]] hessianF :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f (f a)) hessianF f = decomposeFunctor . jacobian (ComposeFunctor . jacobian (fmap decomposeMode . f . fmap composeMode))-
− src/Numeric/AD/Mode/Wengert.hs
@@ -1,194 +0,0 @@-{-# LANGUAGE Rank2Types, TemplateHaskell, BangPatterns, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}--------------------------------------------------------------------------------- |--- Module      :  Numeric.AD.Mode.Wengert--- Copyright   :  (c) Edward Kmett 2010--- License     :  BSD3--- Maintainer  :  ekmett@gmail.com--- Stability   :  experimental--- Portability :  GHC only------ Reverse-mode automatic differentiation using Wengert lists and Data.Reflection-----------------------------------------------------------------------------------module Numeric.AD.Mode.Wengert-    (-    -- * Gradient-      grad-    , grad'-    , gradWith-    , gradWith'--    -- * Jacobian-    , jacobian-    , jacobian'-    , jacobianWith-    , jacobianWith'--    -- * Hessian-    , hessian-    , hessianF--    -- * Derivatives-    , diff-    , diff'-    , diffF-    , diffF'-    ) where--import Control.Applicative ((<$>))-import Data.Traversable (Traversable)--import Numeric.AD.Types-import Numeric.AD.Internal.Classes-import Numeric.AD.Internal.Composition-import Numeric.AD.Internal.Wengert-import Numeric.AD.Internal.Var---- | The 'grad' function calculates the gradient of a non-scalar-to-scalar function with reverse-mode AD in a single pass.--------- >>> grad (\[x,y,z] -> x*y+z) [1,2,3]--- [2,1,1]-grad :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a-grad f as = reifyTape (snd bds) $ \p -> unbind vs $! partialArrayOf p bds $! f $ vary <$> vs-  where (vs, bds) = bind as-{-# INLINE grad #-}---- | The 'grad'' function calculates the result and gradient of a non-scalar-to-scalar function with reverse-mode AD in a single pass.------ >>> grad' (\[x,y,z] -> x*y+z) [1,2,3]--- (5,[2,1,1])-grad' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f a)-grad' f as = reifyTape (snd bds) $ \p ->-  let r = f (fmap vary vs) in (primal r, unbind vs $! partialArrayOf p bds $! r)-  where (vs, bds) = bind as-{-# INLINE grad' #-}---- | @'grad' g f@ function calculates the gradient of a non-scalar-to-scalar function @f@ with reverse-mode AD in a single pass.--- The gradient is combined element-wise with the argument using the function @g@.------ @--- 'grad' == 'gradWith' (\_ dx -> dx)--- 'id' == 'gradWith' 'const'--- @-gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f b-gradWith g f as = reifyTape (snd bds) $ \p -> unbindWith g vs $! partialArrayOf p bds $! f $ vary <$> vs-  where (vs,bds) = bind as-{-# INLINE gradWith #-}---- | @'grad'' g f@ calculates the result and gradient of a non-scalar-to-scalar function @f@ with reverse-mode AD in a single pass--- the gradient is combined element-wise with the argument using the function @g@.------ @--- 'grad'' == 'gradWith'' (\_ dx -> dx)--- @-gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f b)-gradWith' g f as = reifyTape (snd bds) $ \p ->-   let r = f (fmap vary vs) in (primal r, unbindWith g vs $! partialArrayOf p bds $! r)-    where (vs, bds) = bind as-{-# INLINE gradWith' #-}---- | The 'jacobian' function calculates the jacobian of a non-scalar-to-non-scalar function with reverse AD lazily in @m@ passes for @m@ outputs.------ >>> jacobian (\[x,y] -> [y,x,x*y]) [2,1]--- [[0,1],[1,0],[1,2]]-jacobian :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a)-jacobian f as = reifyTape (snd bds) $ \p -> unbind vs . partialArrayOf p bds <$> f (fmap vary vs)-  where (vs, bds) = bind as-{-# INLINE jacobian #-}---- | The 'jacobian'' function calculates both the result and the Jacobian of a nonscalar-to-nonscalar function, using @m@ invocations of reverse AD,--- where @m@ is the output dimensionality. Applying @fmap snd@ to the result will recover the result of 'jacobian'--- | An alias for 'gradF''------ >>> jacobian' (\[x,y] -> [y,x,x*y]) [2,1]--- [(1,[0,1]),(2,[1,0]),(2,[1,2])]-jacobian' :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f a)-jacobian' f as = reifyTape (snd bds) $ \p ->-  let row a = (primal a, unbind vs $! partialArrayOf p bds $! a)-  in row <$> f (vary <$> vs)-  where (vs, bds) = bind as-{-# INLINE jacobian' #-}---- | 'jacobianWith g f' calculates the Jacobian of a non-scalar-to-non-scalar function @f@ with reverse AD lazily in @m@ passes for @m@ outputs.------ Instead of returning the Jacobian matrix, the elements of the matrix are combined with the input using the @g@.------ @--- 'jacobian' == 'jacobianWith' (\_ dx -> dx)--- 'jacobianWith' 'const' == (\f x -> 'const' x '<$>' f x)--- @-jacobianWith :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f b)-jacobianWith g f as = reifyTape (snd bds) $ \p -> unbindWith g vs . partialArrayOf p bds <$> f (fmap vary vs) where-    (vs, bds) = bind as-{-# INLINE jacobianWith #-}---- | 'jacobianWith' g f' calculates both the result and the Jacobian of a nonscalar-to-nonscalar function @f@, using @m@ invocations of reverse AD,--- where @m@ is the output dimensionality. Applying @fmap snd@ to the result will recover the result of 'jacobianWith'------ Instead of returning the Jacobian matrix, the elements of the matrix are combined with the input using the @g@.------ @'jacobian'' == 'jacobianWith'' (\_ dx -> dx)@----jacobianWith' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f b)-jacobianWith' g f as = reifyTape (snd bds) $ \p ->-  let row a = (primal a, unbindWith g vs $! partialArrayOf p bds $! a)-  in row <$> f (vary <$> vs)-  where (vs, bds) = bind as-{-# INLINE jacobianWith' #-}---- | Compute the derivative of a function.------ >>> diff sin 0--- 1.0-diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a-diff f a = reifyTape 1 $ \p -> derivativeOf p $! f (var a 0)-{-# INLINE diff #-}---- | The 'diff'' function calculates the result and derivative, as a pair, of a scalar-to-scalar function.------ >>> diff' sin 0--- (0.0,1.0)------ >>> diff' exp 0--- (1.0,1.0)-diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)-diff' f a = reifyTape 1 $ \p -> derivativeOf' p $! f (var a 0)-{-# INLINE diff' #-}---- | Compute the derivatives of each result of a scalar-to-vector function with regards to its input.------ >>> diffF (\a -> [sin a, cos a]) 0--- [1.0,0.0]----diffF :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f a-diffF f a = reifyTape 1 $ \p -> derivativeOf p <$> f (var a 0)-{-# INLINE diffF #-}---- | Compute the derivatives of each result of a scalar-to-vector function with regards to its input along with the answer.------ >>> diffF' (\a -> [sin a, cos a]) 0--- [(0.0,1.0),(1.0,0.0)]-diffF' :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f (a, a)-diffF' f a = reifyTape 1 $ \p -> derivativeOf' p <$> f (var a 0)-{-# INLINE diffF' #-}---- | Compute the hessian via the jacobian of the gradient. gradient is computed in reverse mode and then the jacobian is computed in reverse mode.------ However, since the @'grad' f :: f a -> f a@ is square this is not as fast as using the forward-mode Jacobian of a reverse mode gradient provided by 'Numeric.AD.hessian'.------ >>> hessian (\[x,y] -> x*y) [1,2]--- [[0,1],[1,0]]-hessian :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f (f a)-hessian f = jacobian (grad (decomposeMode . f . fmap composeMode))---- | Compute the order 3 Hessian tensor on a non-scalar-to-non-scalar function via the reverse-mode Jacobian of the reverse-mode Jacobian of the function.------ Less efficient than 'Numeric.AD.Mode.Mixed.hessianF'.------ >>> hessianF (\[x,y] -> [x*y,x+y,exp x*cos y]) [1,2]--- [[[0.0,1.0],[1.0,0.0]],[[0.0,0.0],[0.0,0.0]],[[-1.1312043837568135,-2.4717266720048188],[-2.4717266720048188,1.1312043837568135]]]-hessianF :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f (f a))-hessianF f = decomposeFunctor . jacobian (ComposeFunctor . jacobian (fmap decomposeMode . f . fmap composeMode))
src/Numeric/AD/Variadic.hs view
@@ -25,5 +25,5 @@     , Grads, vgrads     ) where -import Numeric.AD.Variadic.Reverse+import Numeric.AD.Variadic.Kahn import Numeric.AD.Variadic.Sparse (Grads, vgrads)
+ src/Numeric/AD/Variadic/Kahn.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE Rank2Types, TemplateHaskell, BangPatterns, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}+-----------------------------------------------------------------------------+-- |+-- Module      :  Numeric.AD.Variadic.Kahn+-- Copyright   :  (c) Edward Kmett 2010-2012+-- License     :  BSD3+-- Maintainer  :  ekmett@gmail.com+-- Stability   :  experimental+-- Portability :  non-portable+--+-- Variadic combinators for reverse-mode automatic differentiation.+--+-- Unfortunately, variadicity comes at the expense of being able to use+-- quantification to avoid sensitivity confusion, so be careful when+-- counting the number of @lift@ you use when taking the gradient of a+-- function that takes gradients!+--+-----------------------------------------------------------------------------++module Numeric.AD.Variadic.Kahn+    (+    -- * Unsafe Variadic Gradient+      vgrad, vgrad'+    , Grad+    ) where++import Numeric.AD.Internal.Kahn
− src/Numeric/AD/Variadic/Reverse.hs
@@ -1,27 +0,0 @@-{-# LANGUAGE Rank2Types, TemplateHaskell, BangPatterns, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}--------------------------------------------------------------------------------- |--- Module      :  Numeric.AD.Variadic.Reverse--- Copyright   :  (c) Edward Kmett 2010-2012--- License     :  BSD3--- Maintainer  :  ekmett@gmail.com--- Stability   :  experimental--- Portability :  non-portable------ Variadic combinators for reverse-mode automatic differentiation.------ Unfortunately, variadicity comes at the expense of being able to use--- quantification to avoid sensitivity confusion, so be careful when--- counting the number of @lift@ you use when taking the gradient of a--- function that takes gradients!-----------------------------------------------------------------------------------module Numeric.AD.Variadic.Reverse-    (-    -- * Unsafe Variadic Gradient-      vgrad, vgrad'-    , Grad-    ) where--import Numeric.AD.Internal.Reverse