ad 3.4 → 4.0
raw patch · 39 files changed
+2310/−2407 lines, 39 filesdep +addep +criteriondep +transformersdep ~arraydep ~basedep ~comonad
Dependencies added: ad, criterion, transformers
Dependency ranges changed: array, base, comonad, erf, free, mtl, reflection, tagged, template-haskell
Files
- .ghci +1/−1
- .travis.yml +2/−2
- CHANGELOG.markdown +6/−0
- README.markdown +10/−8
- ad.cabal +33/−28
- bench/BlackScholes.hs +74/−0
- src/Numeric/AD.hs +135/−119
- src/Numeric/AD/Halley.hs +23/−18
- src/Numeric/AD/Internal/Classes.hs +0/−343
- src/Numeric/AD/Internal/Combinators.hs +18/−5
- src/Numeric/AD/Internal/Composition.hs +0/−208
- src/Numeric/AD/Internal/Dense.hs +133/−132
- src/Numeric/AD/Internal/Forward.hs +150/−144
- src/Numeric/AD/Internal/Forward/Double.hs +227/−0
- src/Numeric/AD/Internal/Identity.hs +35/−133
- src/Numeric/AD/Internal/Jet.hs +0/−97
- src/Numeric/AD/Internal/Kahn.hs +197/−167
- src/Numeric/AD/Internal/On.hs +49/−0
- src/Numeric/AD/Internal/Reverse.hs +138/−88
- src/Numeric/AD/Internal/Sparse.hs +153/−137
- src/Numeric/AD/Internal/Tower.hs +103/−74
- src/Numeric/AD/Internal/Types.hs +0/−74
- src/Numeric/AD/Internal/Var.hs +0/−74
- src/Numeric/AD/Jacobian.hs +39/−0
- src/Numeric/AD/Jet.hs +101/−0
- src/Numeric/AD/Mode.hs +61/−0
- src/Numeric/AD/Mode/Directed.hs +58/−59
- src/Numeric/AD/Mode/Forward.hs +74/−82
- src/Numeric/AD/Mode/Forward/Double.hs +170/−0
- src/Numeric/AD/Mode/Kahn.hs +86/−75
- src/Numeric/AD/Mode/Reverse.hs +64/−58
- src/Numeric/AD/Mode/Sparse.hs +52/−43
- src/Numeric/AD/Mode/Tower.hs +48/−56
- src/Numeric/AD/Newton.hs +68/−52
- src/Numeric/AD/Types.hs +0/−50
- src/Numeric/AD/Variadic.hs +0/−28
- src/Numeric/AD/Variadic/Kahn.hs +0/−26
- src/Numeric/AD/Variadic/Sparse.hs +0/−26
- tests/doctests.hs +2/−0
.ghci view
@@ -1,1 +1,1 @@-:set -isrc -idist/build/autogen -optP-include -optPdist/build/autogen/cabal_macros.h+:set -isrc -idist/build/autogen -optP-include -optPdist/build/autogen/cabal_macros.h -optP-include -optPinclude
.travis.yml view
@@ -12,7 +12,7 @@ script: - $script- - hlint src --cpp-define HLINT+ - hlint src --cpp-define HLINT --cpp-include include notifications: irc:@@ -23,4 +23,4 @@ - "\x0313ad\x03/\x0306%{branch}\x03 \x0314%{commit}\x03 %{build_url} %{message}" env:- - mode="--enable-tests" script="cabal test"+ - mode="--enable-tests" script="cabal test --show-details=always"
CHANGELOG.markdown view
@@ -1,6 +1,12 @@+4.0+---+* An overhaul permitting monomorphic modes was completed by @alang9.+* Add a `ForwardDouble` monomorphic mode+ 3.4 --- * Added support for `erf` and `inverf`, etc. from `Data.Number.Erf`.+* Split the infinitesimal and mode into two separate parameters to facilitate inlining and easier extension of the API. 3.3.1 -----
README.markdown view
@@ -23,7 +23,7 @@ You can compute derivatives of functions - Prelude Numeric.AD> diff sin 0 {-# cos 0 #-}+ Prelude Numeric.AD> diff sin 0 {- cos 0 -} 1.0 Or both the answer and the derivative of a function:@@ -59,30 +59,32 @@ Prelude Numeric.AD> take 10 $ diffs sin 1 [0.8414709848078965,0.5403023058681398,-0.8414709848078965,-0.5403023058681398,0.8414709848078965,0.5403023058681398,-0.8414709848078965,-0.5403023058681398,0.8414709848078965,0.5403023058681398] -or if your function takes multiple inputs, you can use grads, which returns an 'f-branching stream' of derivatives. Somewhat more intuitive answers can be obtained by converting the stream into the-polymorphically recursive `Tensors` data type. With that we can look at a single 'layer' of the answer at a time:+or if your function takes multiple inputs, you can use grads, which returns an 'f-branching stream' of derivatives, that you can+inspect lazily. Somewhat more intuitive answers can be obtained by converting the stream into the polymorphically recursive +`Jet` data type. With that we can look at a single "layer" of the answer at a time: The answer: - Prelude Numeric.AD> headJet $ tensors $ grads (\[x,y] -> exp (x * y)) [1,2]+ Prelude Numeric.AD Numeric.AD.Types> headJet $ jet $ grads (\[x,y] -> exp (x * y)) [1,2] 7.38905609893065 The gradient: - Prelude Numeric.AD> headJet $ tailJet $ tensors $ grads (\[x,y] -> exp (x * y)) [1,2]+ Prelude Numeric.AD Numeric.AD.Types> headJet $ tailJet $ jet $ grads (\[x,y] -> exp (x * y)) [1,2] [14.7781121978613,7.38905609893065] The hessian (n * n matrix of 2nd derivatives) - Prelude Numeric.AD> headJet $ tailJet $ tailJet $ tensors $ grads (\[x,y] -> exp (x * y)) [1,2]+ Prelude Numeric.AD Numeric.AD.Types> headJet $ tailJet $ tailJet $ jet $ grads (\[x,y] -> exp (x * y)) [1,2] [[29.5562243957226,22.16716829679195],[22.16716829679195,7.38905609893065]] Or even higher order tensors of derivatives. - Prelude Numeric.AD> headJet $ tailJet $ tailJet $ tailJet $ tensors $ grads (\[x,y] -> exp (x * y)) [1,2]+ Prelude Numeric.AD Numeric.AD.Types> headJet $ tailJet $ tailJet $ tailJet $ jet $ grads (\[x,y] -> exp (x * y)) [1,2] [[[59.1124487914452,44.3343365935839],[44.3343365935839,14.7781121978613]],[[44.3343365935839,14.7781121978613],[14.7781121978613,7.38905609893065]]] -Note the redundant values caused by the various symmetries in the tensors. The 'ad' library is careful to compute each distinct derivative only once and to share the resulting thunks.+Note the redundant values caused by the various symmetries in the tensors. The `ad` library is careful to compute +each distinct derivative only once, lazily and to share the resulting computation. Overview --------
ad.cabal view
@@ -1,8 +1,8 @@ name: ad-version: 3.4+version: 4.0 license: BSD3 license-File: LICENSE-copyright: (c) Edward Kmett 2010-2013,+copyright: (c) Edward Kmett 2010-2014, (c) Barak Pearlmutter and Jeffrey Mark Siskind 2008-2009 author: Edward Kmett maintainer: ekmett@gmail.com@@ -81,6 +81,7 @@ library extensions: CPP hs-source-dirs: src+ include-dirs: include other-extensions: BangPatterns@@ -99,51 +100,48 @@ UndecidableInstances build-depends:- array >= 0.2 && < 0.5,- base == 4.*,- comonad >= 3,- containers >= 0.2 && < 0.6,- data-reify >= 0.6 && < 0.7,- erf >= 2.0 && < 2.1,- free >= 3,- mtl >= 2,- reflection >= 1.1.6,- tagged >= 0.4.2.1,- template-haskell >= 2.5 && < 2.9+ array >= 0.2 && < 0.6,+ base >= 4.5 && < 5,+ comonad >= 4 && < 5,+ containers >= 0.2 && < 0.6,+ data-reify >= 0.6 && < 0.7,+ erf >= 2.0 && < 2.1,+ free >= 4.6.1 && < 5,+ mtl >= 2 && < 2.2,+ reflection >= 1.4 && < 2,+ tagged >= 0.7 && < 1,+ template-haskell,+ transformers >= 0.3 && < 0.4 exposed-modules: Numeric.AD- Numeric.AD.Types - Numeric.AD.Newton Numeric.AD.Halley+ Numeric.AD.Jacobian+ Numeric.AD.Jet+ Numeric.AD.Newton + Numeric.AD.Mode Numeric.AD.Mode.Directed Numeric.AD.Mode.Forward+ Numeric.AD.Mode.Forward.Double Numeric.AD.Mode.Kahn Numeric.AD.Mode.Reverse Numeric.AD.Mode.Tower Numeric.AD.Mode.Sparse - Numeric.AD.Variadic- Numeric.AD.Variadic.Kahn- Numeric.AD.Variadic.Sparse-- Numeric.AD.Internal.Classes- Numeric.AD.Internal.Combinators+ Numeric.AD.Internal.Dense Numeric.AD.Internal.Forward- Numeric.AD.Internal.Tower+ Numeric.AD.Internal.Forward.Double+ Numeric.AD.Internal.Identity Numeric.AD.Internal.Kahn+ Numeric.AD.Internal.On Numeric.AD.Internal.Reverse- Numeric.AD.Internal.Var Numeric.AD.Internal.Sparse- Numeric.AD.Internal.Dense- Numeric.AD.Internal.Composition+ Numeric.AD.Internal.Tower other-modules:- Numeric.AD.Internal.Types- Numeric.AD.Internal.Jet- Numeric.AD.Internal.Identity+ Numeric.AD.Internal.Combinators if flag(lib-Werror) ghc-options: -Werror@@ -166,3 +164,10 @@ if impl(ghc<7.6) ghc-options: -Werror hs-source-dirs: tests++benchmark blackscholes+ type: exitcode-stdio-1.0+ main-is: BlackScholes.hs+ hs-source-dirs: bench+ build-depends: base, ad, erf, criterion+ ghc-options: -fspec-constr -fdicts-cheap -O2
+ bench/BlackScholes.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE RankNTypes #-}+import Criterion.Main+import Data.Number.Erf+import qualified Numeric.AD as Mixed+import qualified Numeric.AD.Mode.Forward as Forward+import qualified Numeric.AD.Mode.Kahn as Kahn+import qualified Numeric.AD.Mode.Reverse as Reverse+import qualified Numeric.AD.Mode.Sparse as Sparse++blackScholes :: (Erf a) => a -> a -> a -> a -> a -> (a, a)+blackScholes r s v t k = (put, call)+ where+ put = k * exp (negate r * t) - s + call+ call = normcdf (negate d2) * k * exp (negate r * t) - normcdf (negate d1) * s+ d1 = (log (s / k) + (r + v * v / 2) * t) / (v * sqrt t)+ d2 = d1 - v * t++bs :: Erf a => [a] -> (a, a)+bs [r', s', v', t', k'] = blackScholes r' s' v' t' k'++fromPair :: (t, t) -> [t]+fromPair (a, b) = [a, b]++runF :: Num a => (a -> a -> a -> a -> a -> b) -> Int -> [b]+runF f n =+ [ f r s v t k+ | r <- xs, s <- xs, v <- xs, t <- xs, k <- xs]+ where+ xs = map fromIntegral [1..n]++runFloat :: (Float -> Float -> Float -> Float -> Float -> b) -> Int -> [b]+runFloat = runF++runDouble :: (Double -> Double -> Double -> Double -> Double -> b) -> Int -> [b]+runDouble = runF++main = defaultMain+ [ bgroup "Forward"+ [ bench "greeks Double" $ nf (runDouble $ \r s v t k -> Forward.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ , bench "greeks Float" $ nf (runFloat $ \r s v t k -> Forward.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ ]+ , bgroup "Kahn"+ [ bench "greeks Double" $ nf (runDouble $ \r s v t k -> Kahn.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ , bench "higherGreeks Double" $ nf (runDouble $ \r s v t k -> (Kahn.hessian (fst . bs) [r, s, v, t, k], Kahn.hessian (snd . bs) [r, s, v, t, k])) 2+ , bench "highererGreeks Double" $ nf (runDouble $ \r s v t k -> Kahn.hessianF (fromPair . bs) [r, s, v, t, k]) 2+ , bench "greeks Float" $ nf (runFloat $ \r s v t k -> Kahn.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ , bench "higherGreeks Float" $ nf (runFloat $ \r s v t k -> (Kahn.hessian (fst . bs) [r, s, v, t, k], Kahn.hessian (snd . bs) [r, s, v, t, k])) 2+ , bench "highererGreeks Float" $ nf (runFloat $ \r s v t k -> Kahn.hessianF (fromPair . bs) [r, s, v, t, k]) 2+ ]+ , bgroup "Reverse"+ [ bench "greeks Double" $ nf (runDouble $ \r s v t k -> Reverse.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ , bench "higherGreeks Double" $ nf (runDouble $ \r s v t k -> (Reverse.hessian (fst . bs) [r, s, v, t, k], Reverse.hessian (snd . bs) [r, s, v, t, k])) 2+ , bench "highererGreeks Double" $ nf (runDouble $ \r s v t k -> Reverse.hessianF (fromPair . bs) [r, s, v, t, k]) 2+ , bench "greeks Float" $ nf (runFloat $ \r s v t k -> Reverse.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ , bench "higherGreeks Float" $ nf (runFloat $ \r s v t k -> (Reverse.hessian (fst . bs) [r, s, v, t, k], Reverse.hessian (snd . bs) [r, s, v, t, k])) 2+ , bench "highererGreeks Float" $ nf (runFloat $ \r s v t k -> Reverse.hessianF (fromPair . bs) [r, s, v, t, k]) 2+ ]+ , bgroup "Sparse"+ [ bench "greeks Double" $ nf (runDouble $ \r s v t k -> Sparse.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ , bench "higherGreeks Double" $ nf (runDouble $ \r s v t k -> (Sparse.hessian (fst . bs) [r, s, v, t, k], Sparse.hessian (snd . bs) [r, s, v, t, k])) 2+ , bench "highererGreeks Double" $ nf (runDouble $ \r s v t k -> Sparse.hessianF (fromPair . bs) [r, s, v, t, k]) 2+ , bench "greeks Float" $ nf (runFloat $ \r s v t k -> Sparse.jacobian (fromPair . bs) [r, s, v, t, k]) 2+ , bench "higherGreeks Float" $ nf (runFloat $ \r s v t k -> (Sparse.hessian (fst . bs) [r, s, v, t, k], Sparse.hessian (snd . bs) [r, s, v, t, k])) 2+ , bench "highererGreeks Float" $ nf (runFloat $ \r s v t k -> Sparse.hessianF (fromPair . bs) [r, s, v, t, k]) 2+ ]+-- , bgroup "Mixed"+-- [ bench "greeks Double" $ nf (runDouble $ \r s v t k -> Mixed.jacobian (fromPair . bs) [r, s, v, t, k]) 2+-- , bench "higherGreeks Double" $ nf (runDouble $ \r s v t k -> (Mixed.hessian (fst . bs) [r, s, v, t, k], Mixed.hessian (snd . bs) [r, s, v, t, k])) 2+-- , bench "highererGreeks Double" $ nf (runDouble $ \r s v t k -> Mixed.hessianF (fromPair . bs) [r, s, v, t, k]) 2+-- , bench "greeks Float" $ nf (runFloat $ \r s v t k -> Mixed.jacobian (fromPair . bs) [r, s, v, t, k]) 2+-- , bench "higherGreeks Float" $ nf (runFloat $ \r s v t k -> (Mixed.hessian (fst . bs) [r, s, v, t, k], Mixed.hessian (snd . bs) [r, s, v, t, k])) 2+-- , bench "highererGreeks Float" $ nf (runFloat $ \r s v t k -> Mixed.hessianF (fromPair . bs) [r, s, v, t, k]) 2+-- ]+ ]
src/Numeric/AD.hs view
@@ -1,8 +1,10 @@-{-# LANGUAGE Rank2Types, TypeFamilies, PatternGuards #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE PatternGuards #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -39,160 +41,161 @@ ----------------------------------------------------------------------------- module Numeric.AD- (- -- * Gradients (Reverse Mode)- grad- , grad'- , gradWith- , gradWith'+ ( - -- * Higher Order Gradients (Sparse-on-Reverse)- , grads+ -- * AD modes+ Mode(auto)+ , Scalar - -- * Jacobians (Sparse or Reverse)- , jacobian- , jacobian'- , jacobianWith- , jacobianWith'+ -- * Gradients (Reverse Mode)+ , grad+ , grad'+ , gradWith+ , gradWith' - -- * Higher Order Jacobian (Sparse-on-Reverse)- , jacobians+ -- * Higher Order Gradients (Sparse-on-Reverse)+ , grads - -- * Transposed Jacobians (Forward Mode)- , jacobianT- , jacobianWithT+ -- * Variadic Gradients (Sparse or Kahn)+ -- $vgrad+ , Grad , vgrad, vgrad'+ , Grads, vgrads - -- * Hessian (Sparse-On-Reverse)- , hessian- , hessian'+ -- * Jacobians (Sparse or Reverse)+ , jacobian+ , jacobian'+ , jacobianWith+ , jacobianWith' - -- * Hessian Tensors (Sparse or Sparse-On-Reverse)- , hessianF- -- * Hessian Tensors (Sparse)- , hessianF'+ -- * Higher Order Jacobian (Sparse-on-Reverse)+ , jacobians - -- * Hessian Vector Products (Forward-On-Reverse)- , hessianProduct- , hessianProduct'+ -- * Transposed Jacobians (Forward Mode)+ , jacobianT+ , jacobianWithT - -- * Derivatives (Forward Mode)- , diff- , diffF+ -- * Hessian (Sparse-On-Reverse)+ , hessian+ , hessian' - , diff'- , diffF'+ -- * Hessian Tensors (Sparse or Sparse-On-Reverse)+ , hessianF - -- * Derivatives (Tower)- , diffs- , diffsF+ -- * Hessian Tensors (Sparse)+ , hessianF' - , diffs0- , diffs0F+ -- * Hessian Vector Products (Forward-On-Reverse)+ , hessianProduct+ , hessianProduct' - -- * Directional Derivatives (Forward Mode)- , du- , du'- , duF- , duF'+ -- * Derivatives (Forward Mode)+ , diff+ , diffF - -- * Directional Derivatives (Tower)- , dus- , dus0- , dusF- , dus0F+ , diff'+ , diffF' - -- * Taylor Series (Tower)- , taylor- , taylor0+ -- * Derivatives (Tower)+ , diffs+ , diffsF - -- * Maclaurin Series (Tower)- , maclaurin- , maclaurin0+ , diffs0+ , diffs0F - -- * Gradient Descent- , gradientDescent- , gradientAscent- , conjugateGradientDescent- , conjugateGradientAscent- ) where+ -- * Directional Derivatives (Forward Mode)+ , du+ , du'+ , duF+ , duF' -import Data.Traversable (Traversable)-import Data.Foldable (Foldable, foldr')+ -- * Directional Derivatives (Tower)+ , dus+ , dus0+ , dusF+ , dus0F++ -- * Taylor Series (Tower)+ , taylor+ , taylor0++ -- * Maclaurin Series (Tower)+ , maclaurin+ , maclaurin0++ -- * Gradient Descent+ , gradientDescent+ , gradientAscent+ , conjugateGradientDescent+ , conjugateGradientAscent++ ) where+ import Control.Applicative+import Data.Functor.Compose+import Data.Traversable (Traversable)+import Data.Reflection (Reifies)+import Numeric.AD.Internal.Forward (Forward)+import Numeric.AD.Internal.Kahn (Grad, vgrad, vgrad')+import Numeric.AD.Internal.On+import Numeric.AD.Internal.Reverse (Reverse, Tape)+import Numeric.AD.Internal.Sparse (Sparse, Grads, vgrads) -import Numeric.AD.Types-import Numeric.AD.Internal.Composition-import Numeric.AD.Internal.Identity+import Numeric.AD.Mode import Numeric.AD.Mode.Forward- ( diff, diff', diffF, diffF'- , du, du', duF, duF'- , jacobianT, jacobianWithT )+ ( diff, diff', diffF, diffF'+ , du, du', duF, duF'+ , jacobianT, jacobianWithT ) import Numeric.AD.Mode.Tower- ( diffsF, diffs0F, diffs, diffs0- , taylor, taylor0, maclaurin, maclaurin0- , dus, dus0, dusF, dus0F )+ ( diffsF, diffs0F, diffs, diffs0+ , taylor, taylor0, maclaurin, maclaurin0+ , dus, dus0, dusF, dus0F ) import qualified Numeric.AD.Mode.Reverse as Reverse import Numeric.AD.Mode.Reverse- ( grad, grad', gradWith, gradWith')+ ( grad, grad', gradWith, gradWith') -- temporary until we make a full sparse mode import qualified Numeric.AD.Mode.Sparse as Sparse import Numeric.AD.Mode.Sparse- ( grads, jacobians, hessian', hessianF')+ ( grads, jacobians, hessian', hessianF') import Numeric.AD.Newton --- | Calculate the Jacobian of a non-scalar-to-non-scalar function, automatically choosing between forward and reverse mode AD based on the number of inputs and outputs.+-- | Calculate the Jacobian of a non-scalar-to-non-scalar function, automatically choosing between sparse and Reverse mode AD. ----- If you know the relative number of inputs and outputs, consider 'Numeric.AD.Reverse.jacobian' or 'Nuneric.AD.Sparse.jacobian'.-jacobian :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a)+-- If you know that you have relatively many outputs per input, consider using 'Numeric.AD.Sparse.jacobian'.+--+-- >>> jacobian (\[x,y] -> [y,x,x+y,x*y,exp x * sin y]) [pi,1]+-- [[0.0,1.0],[1.0,0.0],[1.0,1.0],[1.0,3.141592653589793],[19.472221418841606,12.502969588876512]]+jacobian :: (Traversable f, Functor g, Num a) => (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> f a -> g (f a) jacobian f bs = snd <$> jacobian' f bs {-# INLINE jacobian #-} -data Nat = Z | S Nat deriving (Eq, Ord)--size :: Foldable f => f a -> Nat-size = foldr' (\_ b -> S b) Z--big :: Nat -> Bool-big (S (S (S (S (S (S (S (S (S (S _)))))))))) = True-big _ = False---- | Calculate both the answer and Jacobian of a non-scalar-to-non-scalar function, automatically choosing between forward- and reverse- mode AD based on the relative, based on the number of inputs+-- | Calculate both the answer and Jacobian of a non-scalar-to-non-scalar function, using reverse-mode AD. ----- If you know the relative number of inputs and outputs, consider 'Numeric.AD.Reverse.jacobian'' or 'Nuneric.AD.Sparse.jacobian''.-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 bs | Z <- n = fmap (\x -> (unprobe x, bs)) (f (probed bs))- | big n = Reverse.jacobian' f bs- | otherwise = Sparse.jacobian' f bs- where- n = size bs+-- If you have relatively many outputs per input, consider using 'Numeric.AD.Sparse.jacobian''.+jacobian' :: (Traversable f, Functor g, Num a) => (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> f a -> g (a, f a)+jacobian' = Reverse.jacobian' {-# INLINE jacobian' #-} --- | @'jacobianWith' g f@ calculates the Jacobian of a non-scalar-to-non-scalar function, automatically choosing between forward and reverse mode AD based on the number of inputs and outputs.+-- | @'jacobianWith' g f@ calculates the Jacobian of a non-scalar-to-non-scalar function, using Reverse mode AD. -- -- The resulting Jacobian matrix is then recombined element-wise with the input using @g@. ----- If you know the relative number of inputs and outputs, consider 'Numeric.AD.Reverse.jacobianWith' or 'Nuneric.AD.Sparse.jacobianWith'.-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)+-- If you know that you have relatively many outputs per input, consider using 'Numeric.AD.Sparse.jacobianWith'.+jacobianWith :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> f a -> g (f b) jacobianWith g f bs = snd <$> jacobianWith' g f bs {-# INLINE jacobianWith #-} --- | @'jacobianWith'' g f@ calculates the answer and Jacobian of a non-scalar-to-non-scalar function, automatically choosing between sparse and reverse mode AD based on the number of inputs and outputs.+-- | @'jacobianWith'' g f@ calculates the answer and Jacobian of a non-scalar-to-non-scalar function, using Reverse mode AD. -- -- The resulting Jacobian matrix is then recombined element-wise with the input using @g@. ----- If you know the relative number of inputs and outputs, consider 'Numeric.AD.Reverse.jacobianWith'' or 'Nuneric.AD.Sparse.jacobianWith''.-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 bs- | Z <- n = fmap (\x -> (unprobe x, undefined <$> bs)) (f (probed bs))- | big n = Reverse.jacobianWith' g f bs- | otherwise = Sparse.jacobianWith' g f bs- where- n = size bs+-- If you know that you have relatively many outputs per input, consider using 'Numeric.AD.Sparse.jacobianWith''.+jacobianWith' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> f a -> g (a, f b)+jacobianWith' = Reverse.jacobianWith' {-# INLINE jacobianWith' #-} -- | @'hessianProduct' f wv@ computes the product of the hessian @H@ of a non-scalar-to-scalar function @f@ at @w = 'fst' <$> wv@ with a vector @v = snd <$> wv@ using \"Pearlmutter\'s method\" from <http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.29.6143>, which states:@@ -201,23 +204,36 @@ -- -- Or in other words, we take the directional derivative of the gradient. The gradient is calculated in reverse mode, then the directional derivative is calculated in forward mode. ---hessianProduct :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> f a-hessianProduct f = duF (grad (decomposeMode . f . fmap composeMode))+hessianProduct :: (Traversable f, Num a) => (forall s s'. Reifies s Tape => f (On (Reverse (Forward a s') s)) -> On (Reverse (Forward a s') s)) -> f (a, a) -> f a+hessianProduct f = duF (grad (off . f . fmap On)) -- | @'hessianProduct'' f wv@ computes both the gradient of a non-scalar-to-scalar @f@ at @w = 'fst' <$> wv@ and the product of the hessian @H@ at @w@ with a vector @v = snd <$> wv@ using \"Pearlmutter's method\". The outputs are returned wrapped in the same functor. -- -- > H v = (d/dr) grad_w (w + r v) | r = 0 -- -- Or in other words, we return the gradient and the directional derivative of the gradient. The gradient is calculated in reverse mode, then the directional derivative is calculated in forward mode.-hessianProduct' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> f (a, a)-hessianProduct' f = duF' (grad (decomposeMode . f . fmap composeMode))+hessianProduct' :: (Traversable f, Num a) => (forall s s'. Reifies s Tape => f (On (Reverse (Forward a s') s)) -> On (Reverse (Forward a s') s)) -> f (a, a) -> f (a, a)+hessianProduct' f = duF' (grad (off . f . fmap On)) -- | Compute the Hessian via the Jacobian of the gradient. gradient is computed in reverse mode and then the Jacobian is computed in sparse (forward) mode.-hessian :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f (f a)-hessian f = Sparse.jacobian (grad (decomposeMode . f . fmap composeMode))+--+-- >>> hessian (\[x,y] -> x*y) [1,2]+-- [[0,1],[1,0]]+hessian :: (Traversable f, Num a) => (forall s s'. Reifies s Tape => f (On (Reverse (Sparse a s') s)) -> On (Reverse (Sparse a s') s)) -> f a -> f (f a)+hessian f = Sparse.jacobian (grad (off . f . fmap On)) --- | Compute the order 3 Hessian tensor on a non-scalar-to-non-scalar function using 'Sparse' or 'Sparse'-on-'Reverse'-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 as- | big (size as) = decomposeFunctor $ Sparse.jacobian (ComposeFunctor . Reverse.jacobian (fmap decomposeMode . f . fmap composeMode)) as- | otherwise = Sparse.hessianF f as+-- | Compute the order 3 Hessian tensor on a non-scalar-to-non-scalar function using 'Sparse'-on-'Reverse'+--+-- >>> 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 s'. Reifies s Tape => f (On (Reverse (Sparse a s') s)) -> g (On (Reverse (Sparse a s') s))) -> f a -> g (f (f a))+hessianF f as = getCompose $ Sparse.jacobian (Compose . Reverse.jacobian (fmap off . f . fmap On)) as++-- $vgrad+--+-- Variadic combinators for variadic mixed-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 'auto' calls you use when taking the gradient+-- of a function that takes gradients!
src/Numeric/AD/Halley.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE Rank2Types, ScopedTypeVariables #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Halley--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -16,20 +16,25 @@ ----------------------------------------------------------------------------- module Numeric.AD.Halley- (- -- * Halley's Method (Tower AD)- findZero- , inverse- , fixedPoint- , extremum- ) where+ (+ -- * Halley's Method (Tower AD)+ findZero+ , inverse+ , fixedPoint+ , extremum+ ) where import Prelude hiding (all)-import Numeric.AD.Types+import Numeric.AD.Internal.Forward (Forward)+import Numeric.AD.Internal.On+import Numeric.AD.Internal.Tower (Tower)+import Numeric.AD.Mode import Numeric.AD.Mode.Tower (diffs0) import Numeric.AD.Mode.Forward (diff) -- , diff')-import Numeric.AD.Internal.Composition +-- $setup+-- >>> import Data.Complex+ -- | The 'findZero' function finds a zero of a scalar function using -- Halley's method; its output is a stream of increasingly accurate -- results. (Modulo the usual caveats.) If the stream becomes constant@@ -40,10 +45,9 @@ -- >>> take 10 $ findZero (\x->x^2-4) 1 -- [1.0,1.8571428571428572,1.9997967892704736,1.9999999999994755,2.0] ----- >>> import Data.Complex -- >>> last $ take 10 $ findZero ((+1).(^2)) (1 :+ 1) -- 0.0 :+ 1.0-findZero :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+findZero :: (Fractional a, Eq a) => (forall s. Tower a s -> Tower a s) -> a -> [a] findZero f = go where go x = x : if x == xn then [] else go xn where (y:y':y'':_) = diffs0 f x@@ -57,7 +61,7 @@ -- -- Note: the @take 10 $ inverse sqrt 1 (sqrt 10)@ example that works for Newton's method -- fails with Halley's method because the preconditions do not hold!-inverse :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> a -> [a]+inverse :: (Fractional a, Eq a) => (forall s. Tower a s -> Tower a s) -> a -> a -> [a] inverse f x0 y = findZero (\x -> f x - auto y) x0 {-# INLINE inverse #-} @@ -70,10 +74,11 @@ -- -- >>> last $ take 10 $ fixedPoint cos 1 -- 0.7390851332151607-fixedPoint :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+fixedPoint :: (Fractional a, Eq a) => (forall s. Tower a s -> Tower a s) -> a -> [a] fixedPoint f = findZero (\x -> f x - x) {-# INLINE fixedPoint #-} + -- | The 'extremum' function finds an extremum of a scalar -- function using Halley's method; produces a stream of increasingly -- accurate results. (Modulo the usual caveats.) If the stream becomes@@ -81,6 +86,6 @@ -- -- >>> take 10 $ extremum cos 1 -- [1.0,0.29616942658570555,4.59979519460002e-3,1.6220740159042513e-8,0.0]-extremum :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]-extremum f = findZero (diff (decomposeMode . f . composeMode))+extremum :: (Fractional a, Eq a) => (forall s s'. On (Forward (Tower a s') s) -> On (Forward (Tower a s') s)) -> a -> [a]+extremum f = findZero (diff (off . f . On)) {-# INLINE extremum #-}
− src/Numeric/AD/Internal/Classes.hs
@@ -1,343 +0,0 @@-{-# LANGUAGE Rank2Types, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, PatternGuards, CPP #-}-{-# LANGUAGE FlexibleContexts, FunctionalDependencies, UndecidableInstances, GeneralizedNewtypeDeriving, TemplateHaskell #-}--- {-# OPTIONS_HADDOCK hide #-}--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Internal.Classes--- Copyright : (c) Edward Kmett 2010--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : GHC only-----------------------------------------------------------------------------------module Numeric.AD.Internal.Classes- (- -- * AD modes- Mode(..)- , one- -- * Automatically Deriving AD- , Jacobian(..)- , Primal(..)- , deriveLifted- , deriveNumeric- , Lifted(..)- , Iso(..)- ) where--import Control.Applicative hiding ((<**>))-import Data.Char-import Data.Function (on)-import Data.Number.Erf-import Language.Haskell.TH--infixr 8 **!, <**>-infixl 7 *!, /!, ^*, *^, ^/-infixl 6 +!, -!, <+>-infix 4 ==!--class Iso a b where- iso :: f a -> f b- osi :: f b -> f a--instance Iso a a where- iso = id- osi = id--class Lifted t where- showsPrec1 :: (Num a, Show a) => Int -> t a -> ShowS- (==!) :: (Num a, Eq a) => t a -> t a -> Bool- compare1 :: (Num a, Ord a) => t a -> t a -> Ordering- fromInteger1 :: Num a => Integer -> t a- (+!),(-!),(*!) :: Num a => t a -> t a -> t a- negate1, abs1, signum1 :: Num a => t a -> t a- (/!) :: Fractional a => t a -> t a -> t a- recip1 :: Fractional a => t a -> t a- fromRational1 :: Fractional a => Rational -> t a- toRational1 :: Real a => t a -> Rational -- unsafe- pi1 :: Floating a => t a- exp1, log1, sqrt1 :: Floating a => t a -> t a- (**!), logBase1 :: Floating a => t a -> t a -> t a- sin1, cos1, tan1, asin1, acos1, atan1 :: Floating a => t a -> t a- sinh1, cosh1, tanh1, asinh1, acosh1, atanh1 :: Floating a => t a -> t a- properFraction1 :: (RealFrac a, Integral b) => t a -> (b, t a)- truncate1, round1, ceiling1, floor1 :: (RealFrac a, Integral b) => t a -> b- floatRadix1 :: RealFloat a => t a -> Integer- floatDigits1 :: RealFloat a => t a -> Int- floatRange1 :: RealFloat a => t a -> (Int, Int)- decodeFloat1 :: RealFloat a => t a -> (Integer, Int)- encodeFloat1 :: RealFloat a => Integer -> Int -> t a- exponent1 :: RealFloat a => t a -> Int- significand1 :: RealFloat a => t a -> t a- scaleFloat1 :: RealFloat a => Int -> t a -> t a- isNaN1, isInfinite1, isDenormalized1, isNegativeZero1, isIEEE1 :: RealFloat a => t a -> Bool- atan21 :: RealFloat a => t a -> t a -> t a- succ1, pred1 :: (Num a, Enum a) => t a -> t a- toEnum1 :: (Num a, Enum a) => Int -> t a- fromEnum1 :: (Num a, Enum a) => t a -> Int- enumFrom1 :: (Num a, Enum a) => t a -> [t a]- enumFromThen1 :: (Num a, Enum a) => t a -> t a -> [t a]- enumFromTo1 :: (Num a, Enum a) => t a -> t a -> [t a]- enumFromThenTo1 :: (Num a, Enum a) => t a -> t a -> t a -> [t a]- minBound1 :: (Num a, Bounded a) => t a- maxBound1 :: (Num a, Bounded a) => t a- erf1 :: Erf a => t a -> t a- erfc1 :: Erf a => t a -> t a- normcdf1 :: Erf a => t a -> t a- inverf1 :: InvErf a => t a -> t a- inverfc1 :: InvErf a => t a -> t a- invnormcdf1 :: InvErf a => t a -> t a--class Lifted t => Mode t where- -- | allowed to return False for items with a zero derivative, but we'll give more NaNs than strictly necessary- isKnownConstant :: t a -> Bool- isKnownConstant _ = False-- -- | allowed to return False for zero, but we give more NaN's than strictly necessary then- isKnownZero :: Num a => t a -> Bool- isKnownZero _ = False-- -- | Embed a constant- auto :: Num a => a -> t a-- -- | Vector sum- (<+>) :: Num a => t a -> t a -> t a-- -- | Scalar-vector multiplication- (*^) :: Num a => a -> t a -> t a-- -- | Vector-scalar multiplication- (^*) :: Num a => t a -> a -> t a-- -- | Scalar division- (^/) :: Fractional a => t a -> a -> t a-- -- | Exponentiation, this should be overloaded if you can figure out anything about what is constant!- (<**>) :: Floating a => t a -> t a -> t a--- x <**> y = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y-- -- | > 'zero' = 'lift' 0- zero :: Num a => t a-- a *^ b = auto a *! b- a ^* b = a *! auto b-- a ^/ b = a ^* recip b-- zero = auto 0--one :: (Mode t, Num a) => t a-one = auto 1-{-# INLINE one #-}--negOne :: (Mode t, Num a) => t a-negOne = auto (-1)-{-# INLINE negOne #-}---- | 'Primal' is used by 'deriveMode' but is not exposed--- via the 'Mode' class to prevent its abuse by end users--- via the AD data type.------ It provides direct access to the result, stripped of its derivative information,--- but this is unsafe in general as (auto . primal) would discard derivative--- information. The end user is protected from accidentally using this function--- by the universal quantification on the various combinators we expose.--class Primal t where- primal :: Num a => t a -> a---- | 'Jacobian' is used by 'deriveMode' but is not exposed--- via 'Mode' to prevent its abuse by end users--- via the 'AD' data type.-class (Mode t, Mode (D t)) => Jacobian t where- type D t :: * -> *-- unary :: Num a => (a -> a) -> D t a -> t a -> t a- lift1 :: Num a => (a -> a) -> (D t a -> D t a) -> t a -> t a- lift1_ :: Num a => (a -> a) -> (D t a -> D t a -> D t a) -> t a -> t a-- binary :: Num a => (a -> a -> a) -> D t a -> D t a -> t a -> t a -> t a- lift2 :: Num a => (a -> a -> a) -> (D t a -> D t a -> (D t a, D t a)) -> t a -> t a -> t a- lift2_ :: Num a => (a -> a -> a) -> (D t a -> D t a -> D t a -> (D t a, D t a)) -> t a -> t a -> t a--withPrimal :: (Jacobian t, Num a) => t a -> a -> t a-withPrimal t a = unary (const a) one t-{-# INLINE withPrimal #-}--fromBy :: (Jacobian t, Num a) => t a -> t a -> Int -> a -> t a-fromBy a delta n x = binary (\_ _ -> x) one (fromIntegral1 n) a delta--fromIntegral1 :: (Integral n, Lifted t, Num a) => n -> t a-fromIntegral1 = fromInteger1 . fromIntegral-{-# INLINE fromIntegral1 #-}--square1 :: (Lifted t, Num a) => t a -> t a-square1 x = x *! x-{-# INLINE square1 #-}--discrete1 :: (Primal t, Num a) => (a -> c) -> t a -> c-discrete1 f x = f (primal x)-{-# INLINE discrete1 #-}--discrete2 :: (Primal t, Num a) => (a -> a -> c) -> t a -> t a -> c-discrete2 f x y = f (primal x) (primal y)-{-# INLINE discrete2 #-}--discrete3 :: (Primal t, Num a) => (a -> a -> a -> d) -> t a -> t a -> t a -> d-discrete3 f x y z = f (primal x) (primal y) (primal z)-{-# INLINE discrete3 #-}---- | @'deriveLifted' t@ provides------ > instance Lifted $t------ given supplied instances for------ > instance Lifted $t => Primal $t where ...--- > instance Lifted $t => Jacobian $t where ...------ The seemingly redundant @'Lifted' $t@ constraints are caused by Template Haskell staging restrictions.-deriveLifted :: ([Q Pred] -> [Q Pred]) -> Q Type -> Q [Dec]-deriveLifted f _t = do- [InstanceD cxt0 type0 dec0] <- lifted- return <$> instanceD (cxt (f (return <$> cxt0))) (return type0) (return <$> dec0)- where- lifted = [d|- instance Lifted $_t where- (==!) = (==) `on` primal- compare1 = compare `on` primal- maxBound1 = auto maxBound- minBound1 = auto minBound- showsPrec1 d = showsPrec d . primal- fromInteger1 0 = zero- fromInteger1 n = auto (fromInteger n)- (+!) = (<+>) -- binary (+) one one- (-!) = binary (-) one negOne -- TODO: <-> ? as it is, this might be pretty bad for Tower- (*!) = lift2 (*) (\x y -> (y, x))- negate1 = lift1 negate (const negOne)- abs1 = lift1 abs signum1- signum1 = lift1 signum (const zero)- fromRational1 0 = zero- fromRational1 r = auto (fromRational r)- x /! y = x *! recip1 y- recip1 = lift1_ recip (const . negate1 . square1)- pi1 = auto pi- exp1 = lift1_ exp const- log1 = lift1 log recip1- logBase1 x y = log1 y /! log1 x- sqrt1 = lift1_ sqrt (\z _ -> recip1 (auto 2 *! z))- (**!) = (<**>)- --x **! y- -- | isKnownZero y = 1- -- | isKnownConstant y, y' <- primal y = lift1 (** y') ((y'*) . (**(y'-1))) x- -- | otherwise = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y- sin1 = lift1 sin cos1- cos1 = lift1 cos $ negate1 . sin1- tan1 = lift1 tan $ recip1 . square1 . cos1- asin1 = lift1 asin $ \x -> recip1 (sqrt1 (one -! square1 x))- acos1 = lift1 acos $ \x -> negate1 (recip1 (sqrt1 (one -! square1 x)))- atan1 = lift1 atan $ \x -> recip1 (one +! square1 x)- sinh1 = lift1 sinh cosh1- cosh1 = lift1 cosh sinh1- tanh1 = lift1 tanh $ recip1 . square1 . cosh1- asinh1 = lift1 asinh $ \x -> recip1 (sqrt1 (one +! square1 x))- acosh1 = lift1 acosh $ \x -> recip1 (sqrt1 (square1 x -! one))- atanh1 = lift1 atanh $ \x -> recip1 (one -! square1 x)-- succ1 = lift1 succ (const one)- pred1 = lift1 pred (const one)- toEnum1 = auto . toEnum- fromEnum1 = discrete1 fromEnum- enumFrom1 a = withPrimal a <$> discrete1 enumFrom a- enumFromTo1 a b = withPrimal a <$> discrete2 enumFromTo a b- enumFromThen1 a b = zipWith (fromBy a delta) [0..] $ discrete2 enumFromThen a b where delta = b -! a- enumFromThenTo1 a b c = zipWith (fromBy a delta) [0..] $ discrete3 enumFromThenTo a b c where delta = b -! a-- toRational1 = discrete1 toRational- floatRadix1 = discrete1 floatRadix- floatDigits1 = discrete1 floatDigits- floatRange1 = discrete1 floatRange- decodeFloat1 = discrete1 decodeFloat- encodeFloat1 m e = auto (encodeFloat m e)- isNaN1 = discrete1 isNaN- isInfinite1 = discrete1 isInfinite- isDenormalized1 = discrete1 isDenormalized- isNegativeZero1 = discrete1 isNegativeZero- isIEEE1 = discrete1 isIEEE- exponent1 = exponent . primal- scaleFloat1 n = unary (scaleFloat n) (scaleFloat1 n one)- significand1 x = unary significand (scaleFloat1 (- floatDigits1 x) one) x- atan21 = lift2 atan2 $ \vx vy -> let r = recip1 (square1 vx +! square1 vy) in (vy *! r, negate1 vx *! r)- properFraction1 a = (w, a `withPrimal` pb) where- pa = primal a- (w, pb) = properFraction pa- truncate1 = discrete1 truncate- round1 = discrete1 round- ceiling1 = discrete1 ceiling- floor1 = discrete1 floor-- erf1 = lift1 erf $ \x -> (fromInteger1 2 /! sqrt1 pi1) *! exp1 (negate1 x *! x)- erfc1 = lift1 erfc $ \x -> (fromInteger1 (-2) /! sqrt1 pi1) *! exp1 (negate1 x *! x)- normcdf1 = lift1 normcdf $ \x -> (fromInteger1 (-1) /! sqrt1 pi1) *! exp1 (x *! x *! fromRational1 (- recip 2) /! sqrt1 (fromInteger1 2))-- inverf1 = lift1 inverfc $ \x -> recip1 $ (fromInteger1 2 /! sqrt1 pi1) *! exp1 (negate1 x *! x)- inverfc1 = lift1 inverfc $ \x -> recip1 $ negate1 (fromInteger1 2 /! sqrt1 pi1) *! exp1 (negate1 x *! x)- invnormcdf1 = lift1 invnormcdf $ \x -> recip1 $ (fromInteger1 (-1) /! sqrt1 pi1) *! exp1 (x *! x *! fromRational1 (- recip 2) /! sqrt1 (fromInteger1 2)) |]--varA :: Q Type-varA = varT (mkName "a")---- | Find all the members defined in the 'Lifted' data type-liftedMembers :: Q [String]-liftedMembers = do-#ifdef OldClassI- ClassI (ClassD _ _ _ _ ds) <- reify ''Lifted-#else- ClassI (ClassD _ _ _ _ ds) _ <- reify ''Lifted-#endif- return [ nameBase n | SigD n _ <- ds]---- | @'deriveNumeric' f g@ provides the following instances:------ > instance ('Lifted' $f, 'Num' a, 'Enum' a) => 'Enum' ($g a)--- > instance ('Lifted' $f, 'Num' a, 'Eq' a) => 'Eq' ($g a)--- > instance ('Lifted' $f, 'Num' a, 'Ord' a) => 'Ord' ($g a)--- > instance ('Lifted' $f, 'Num' a, 'Bounded' a) => 'Bounded' ($g a)------ > instance ('Lifted' $f, 'Show' a) => 'Show' ($g a)--- > instance ('Lifted' $f, 'Num' a) => 'Num' ($g a)--- > instance ('Lifted' $f, 'Fractional' a) => 'Fractional' ($g a)--- > instance ('Lifted' $f, 'Floating' a) => 'Floating' ($g a)--- > instance ('Lifted' $f, 'RealFloat' a) => 'RealFloat' ($g a)--- > instance ('Lifted' $f, 'RealFrac' a) => 'RealFrac' ($g a)--- > instance ('Lifted' $f, 'Real' a) => 'Real' ($g a)-deriveNumeric :: ([Q Pred] -> [Q Pred]) -> Q Type -> Q [Dec]-deriveNumeric f t = do- members <- liftedMembers- let keep n = nameBase n `elem` members- xs <- lowerInstance keep ((classP ''Num [varA]:) . f) t `mapM` [''Enum, ''Eq, ''Ord, ''Bounded, ''Show]- ys <- lowerInstance keep f t `mapM` [''Num, ''Fractional, ''Floating, ''RealFloat,''RealFrac, ''Real, ''Erf, ''InvErf]- return (xs ++ ys)--lowerInstance :: (Name -> Bool) -> ([Q Pred] -> [Q Pred]) -> Q Type -> Name -> Q Dec-lowerInstance p f t n = do-#ifdef OldClassI- ClassI (ClassD _ _ _ _ ds) <- reify n-#else- ClassI (ClassD _ _ _ _ ds) _ <- reify n-#endif- instanceD (cxt (f [classP n [varA]]))- (conT n `appT` (t `appT` varA))- (concatMap lower1 ds)- where- lower1 :: Dec -> [Q Dec]- lower1 (SigD n' _) | p n'' = [valD (varP n') (normalB (varE n'')) []] where n'' = primed n'- lower1 _ = []-- primed n' = mkName $ base ++ [prime]- where- base = nameBase n'- h = head base- prime | isSymbol h || h `elem` "/*-<>" = '!'- | otherwise = '1'
src/Numeric/AD/Internal/Combinators.hs view
@@ -1,8 +1,8 @@ {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}+{-# OPTIONS_HADDOCK not-home #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Internal.Combinators--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -11,12 +11,16 @@ -- Combinators used internally by @Numeric.AD@ ----------------------------------------------------------------------------- module Numeric.AD.Internal.Combinators- ( zipWithT- , zipWithDefaultT- ) where+ ( zipWithT+ , zipWithDefaultT+ , withPrimal+ , fromBy+ ) where import Data.Traversable (Traversable, mapAccumL) import Data.Foldable (Foldable, toList)+import Numeric.AD.Mode+import Numeric.AD.Jacobian -- | Zip a @'Foldable' f@ with a @'Traversable' g@ assuming @f@ has at least as many entries as @g@. zipWithT :: (Foldable f, Traversable g) => (a -> b -> c) -> f a -> g b -> g c@@ -25,3 +29,12 @@ -- | Zip a @'Foldable' f@ with a @'Traversable' g@ assuming @f@, using a default value after @f@ is exhausted. zipWithDefaultT :: (Foldable f, Traversable g) => a -> (a -> b -> c) -> f a -> g b -> g c zipWithDefaultT z f as = zipWithT f (toList as ++ repeat z)++-- | Used internally to define various 'Enum' combinators.+withPrimal :: Jacobian t => t -> Scalar t -> t+withPrimal t a = unary (const a) 1 t+{-# INLINE withPrimal #-}++-- | Used internally to define various 'Enum' combinators.+fromBy :: Jacobian t => t -> t -> Int -> Scalar t -> t+fromBy a delta n x = binary (\_ _ -> x) 1 (fromIntegral n) a delta
− src/Numeric/AD/Internal/Composition.hs
@@ -1,208 +0,0 @@-{-# LANGUAGE CPP, Rank2Types, TypeFamilies, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, UndecidableInstances, TypeOperators #-}--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Internal.Composition--- Copyright : (c) Edward Kmett 2010--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : GHC only-----------------------------------------------------------------------------------module Numeric.AD.Internal.Composition- ( ComposeFunctor(..)- , ComposeMode(..)- , composeMode- , decomposeMode- ) where--#ifndef MIN_VERSION_base-#define MIN_VERSION_base(x,y,z) 1-#endif--import Control.Applicative hiding ((<**>))-import Data.Data (Data(..), mkDataType, DataType, mkConstr, Constr, constrIndex, Fixity(..))-#if MIN_VERSION_base(4,4,0)-import Data.Typeable (Typeable1(..), Typeable(..), TyCon, mkTyCon3, mkTyConApp, typeOfDefault, gcast1)-#else-import Data.Typeable (Typeable1(..), Typeable(..), TyCon, mkTyCon, mkTyConApp, typeOfDefault, gcast1)-#endif-import Data.Foldable (Foldable(foldMap))-import Data.Traversable (Traversable(traverse))-import Numeric.AD.Internal.Classes-import Numeric.AD.Internal.Types--{-# ANN module "Hlint: ignore Eta reduce" #-}-{-# ANN module "Hlint: ignore Reduce duplication" #-}---- | Functor composition, used to nest the use of jacobian and grad-newtype ComposeFunctor f g a = ComposeFunctor { decomposeFunctor :: f (g a) }--instance (Functor f, Functor g) => Functor (ComposeFunctor f g) where- fmap f (ComposeFunctor a) = ComposeFunctor (fmap (fmap f) a)--instance (Foldable f, Foldable g) => Foldable (ComposeFunctor f g) where- foldMap f (ComposeFunctor a) = foldMap (foldMap f) a--instance (Traversable f, Traversable g) => Traversable (ComposeFunctor f g) where- traverse f (ComposeFunctor a) = ComposeFunctor <$> traverse (traverse f) a--instance (Typeable1 f, Typeable1 g) => Typeable1 (ComposeFunctor f g) where- typeOf1 tfga = mkTyConApp composeFunctorTyCon [typeOf1 (fa tfga), typeOf1 (ga tfga)]- where fa :: t f (g :: * -> *) a -> f a- fa = undefined- ga :: t (f :: * -> *) g a -> g a- ga = undefined--composeFunctorTyCon :: TyCon-#if MIN_VERSION_base(4,4,0)-composeFunctorTyCon = mkTyCon3 "ad" "Numeric.AD.Internal.Composition" "ComposeFunctor"-#else-composeFunctorTyCon = mkTyCon "Numeric.AD.Internal.Composition.ComposeFunctor"-#endif--{-# NOINLINE composeFunctorTyCon #-}--composeFunctorConstr :: Constr-composeFunctorConstr = mkConstr composeFunctorDataType "ComposeFunctor" [] Prefix-{-# NOINLINE composeFunctorConstr #-}--composeFunctorDataType :: DataType-composeFunctorDataType = mkDataType "Numeric.AD.Internal.Composition.ComposeFunctor" [composeFunctorConstr]-{-# NOINLINE composeFunctorDataType #-}--instance (Typeable1 f, Typeable1 g, Data (f (g a)), Data a) => Data (ComposeFunctor f g a) where- gfoldl f z (ComposeFunctor a) = z ComposeFunctor `f` a- toConstr _ = composeFunctorConstr- gunfold k z c = case constrIndex c of- 1 -> k (z ComposeFunctor)- _ -> error "gunfold"- dataTypeOf _ = composeFunctorDataType- dataCast1 f = gcast1 f---- | The composition of two AD modes is an AD mode in its own right-newtype ComposeMode f g a = ComposeMode { runComposeMode :: f (AD g a) }--composeMode :: AD f (AD g a) -> AD (ComposeMode f g) a-composeMode (AD a) = AD (ComposeMode a)--decomposeMode :: AD (ComposeMode f g) a -> AD f (AD g a)-decomposeMode (AD (ComposeMode a)) = AD a--instance (Primal f, Mode g, Primal g) => Primal (ComposeMode f g) where- primal = primal . primal . runComposeMode--instance (Mode f, Mode g) => Mode (ComposeMode f g) where- auto = ComposeMode . auto . auto- ComposeMode a <+> ComposeMode b = ComposeMode (a <+> b)- a *^ ComposeMode b = ComposeMode (auto a *^ b)- ComposeMode a ^* b = ComposeMode (a ^* auto b)- ComposeMode a ^/ b = ComposeMode (a ^/ auto b)- ComposeMode a <**> ComposeMode b = ComposeMode (a <**> b)--instance (Mode f, Mode g) => Lifted (ComposeMode f g) where- showsPrec1 n (ComposeMode a) = showsPrec1 n a- ComposeMode a ==! ComposeMode b = a ==! b- compare1 (ComposeMode a) (ComposeMode b) = compare1 a b- fromInteger1 = ComposeMode . auto . fromInteger1- ComposeMode a +! ComposeMode b = ComposeMode (a +! b)- ComposeMode a -! ComposeMode b = ComposeMode (a -! b)- ComposeMode a *! ComposeMode b = ComposeMode (a *! b)- negate1 (ComposeMode a) = ComposeMode (negate1 a)- abs1 (ComposeMode a) = ComposeMode (abs1 a)- signum1 (ComposeMode a) = ComposeMode (signum1 a)- ComposeMode a /! ComposeMode b = ComposeMode (a /! b)- recip1 (ComposeMode a) = ComposeMode (recip1 a)- fromRational1 = ComposeMode . auto . fromRational1- toRational1 (ComposeMode a) = toRational1 a- pi1 = ComposeMode pi1- exp1 (ComposeMode a) = ComposeMode (exp1 a)- log1 (ComposeMode a) = ComposeMode (log1 a)- sqrt1 (ComposeMode a) = ComposeMode (sqrt1 a)- ComposeMode a **! ComposeMode b = ComposeMode (a **! b)- logBase1 (ComposeMode a) (ComposeMode b) = ComposeMode (logBase1 a b)- sin1 (ComposeMode a) = ComposeMode (sin1 a)- cos1 (ComposeMode a) = ComposeMode (cos1 a)- tan1 (ComposeMode a) = ComposeMode (tan1 a)- asin1 (ComposeMode a) = ComposeMode (asin1 a)- acos1 (ComposeMode a) = ComposeMode (acos1 a)- atan1 (ComposeMode a) = ComposeMode (atan1 a)- sinh1 (ComposeMode a) = ComposeMode (sinh1 a)- cosh1 (ComposeMode a) = ComposeMode (cosh1 a)- tanh1 (ComposeMode a) = ComposeMode (tanh1 a)- asinh1 (ComposeMode a) = ComposeMode (asinh1 a)- acosh1 (ComposeMode a) = ComposeMode (acosh1 a)- atanh1 (ComposeMode a) = ComposeMode (atanh1 a)- properFraction1 (ComposeMode a) = (b, ComposeMode c) where- (b, c) = properFraction1 a- truncate1 (ComposeMode a) = truncate1 a- round1 (ComposeMode a) = round1 a- ceiling1 (ComposeMode a) = ceiling1 a- floor1 (ComposeMode a) = floor1 a- floatRadix1 (ComposeMode a) = floatRadix1 a- floatDigits1 (ComposeMode a) = floatDigits1 a- floatRange1 (ComposeMode a) = floatRange1 a- decodeFloat1 (ComposeMode a) = decodeFloat1 a- encodeFloat1 m e = ComposeMode (encodeFloat1 m e)- exponent1 (ComposeMode a) = exponent1 a- significand1 (ComposeMode a) = ComposeMode (significand1 a)- scaleFloat1 n (ComposeMode a) = ComposeMode (scaleFloat1 n a)- isNaN1 (ComposeMode a) = isNaN1 a- isInfinite1 (ComposeMode a) = isInfinite1 a- isDenormalized1 (ComposeMode a) = isDenormalized1 a- isNegativeZero1 (ComposeMode a) = isNegativeZero1 a- isIEEE1 (ComposeMode a) = isIEEE1 a- atan21 (ComposeMode a) (ComposeMode b) = ComposeMode (atan21 a b)- succ1 (ComposeMode a) = ComposeMode (succ1 a)- pred1 (ComposeMode a) = ComposeMode (pred1 a)- toEnum1 n = ComposeMode (toEnum1 n)- fromEnum1 (ComposeMode a) = fromEnum1 a- enumFrom1 (ComposeMode a) = map ComposeMode $ enumFrom1 a- enumFromThen1 (ComposeMode a) (ComposeMode b) = map ComposeMode $ enumFromThen1 a b- enumFromTo1 (ComposeMode a) (ComposeMode b) = map ComposeMode $ enumFromTo1 a b- enumFromThenTo1 (ComposeMode a) (ComposeMode b) (ComposeMode c) = map ComposeMode $ enumFromThenTo1 a b c- minBound1 = ComposeMode minBound1- maxBound1 = ComposeMode maxBound1- erf1 (ComposeMode a) = ComposeMode (erf1 a)- erfc1 (ComposeMode a) = ComposeMode (erfc1 a)- normcdf1 (ComposeMode a) = ComposeMode (normcdf1 a)- inverf1 (ComposeMode a) = ComposeMode (inverf1 a)- inverfc1 (ComposeMode a) = ComposeMode (inverfc1 a)- invnormcdf1 (ComposeMode a) = ComposeMode (invnormcdf1 a)--instance (Typeable1 f, Typeable1 g) => Typeable1 (ComposeMode f g) where- typeOf1 tfga = mkTyConApp composeModeTyCon [typeOf1 (fa tfga), typeOf1 (ga tfga)]- where fa :: t f (g :: * -> *) a -> f a- fa = undefined- ga :: t (f :: * -> *) g a -> g a- ga = undefined--instance (Typeable1 f, Typeable1 g, Typeable a) => Typeable (ComposeMode f g a) where- typeOf = typeOfDefault--composeModeTyCon :: TyCon-#if MIN_VERSION_base(4,4,0)-composeModeTyCon = mkTyCon3 "ad" "Numeric.AD.Internal.Composition" "ComposeMode"-#else-composeModeTyCon = mkTyCon "Numeric.AD.Internal.Composition.ComposeMode"-#endif-{-# NOINLINE composeModeTyCon #-}--composeModeConstr :: Constr-composeModeConstr = mkConstr composeModeDataType "ComposeMode" [] Prefix-{-# NOINLINE composeModeConstr #-}--composeModeDataType :: DataType-composeModeDataType = mkDataType "Numeric.AD.Internal.Composition.ComposeMode" [composeModeConstr]-{-# NOINLINE composeModeDataType #-}--instance (Typeable1 f, Typeable1 g, Data (f (AD g a)), Data a) => Data (ComposeMode f g a) where- gfoldl f z (ComposeMode a) = z ComposeMode `f` a- toConstr _ = composeModeConstr- gunfold k z c = case constrIndex c of- 1 -> k (z ComposeMode)- _ -> error "gunfold"- dataTypeOf _ = composeModeDataType- dataCast1 f = gcast1 f-
src/Numeric/AD/Internal/Dense.hs view
@@ -1,9 +1,17 @@-{-# LANGUAGE Rank2Types, TypeFamilies, FlexibleContexts, UndecidableInstances, TemplateHaskell, DeriveDataTypeable, BangPatterns #-}--- {-# OPTIONS_HADDOCK hide, prune #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_HADDOCK not-home #-}+ ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Internal.Dense--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -25,162 +33,155 @@ ----------------------------------------------------------------------------- module Numeric.AD.Internal.Dense- ( Dense(..)- , ds- , ds'- , vars- , apply- ) where+ ( Dense(..)+ , ds+ , ds'+ , vars+ , apply+ ) where -import Language.Haskell.TH+import Control.Monad (join)+import Data.Functor import Data.Typeable () import Data.Traversable (Traversable, mapAccumL) import Data.Data ()-import Numeric.AD.Internal.Types+import Data.Number.Erf import Numeric.AD.Internal.Combinators-import Numeric.AD.Internal.Classes import Numeric.AD.Internal.Identity+import Numeric.AD.Jacobian+import Numeric.AD.Mode -data Dense f a- = Lift !a- | Dense !a (f a)- | Zero+data Dense f a s+ = Lift !a+ | Dense !a (f a)+ | Zero -instance Show a => Show (Dense f a) where- showsPrec d (Lift a) = showsPrec d a- showsPrec d (Dense a _) = showsPrec d a- showsPrec _ Zero = showString "0"+type instance Scalar (Dense f a s) = a -ds :: f a -> AD (Dense f) a -> f a-ds _ (AD (Dense _ da)) = da+instance Show a => Show (Dense f a s) where+ showsPrec d (Lift a) = showsPrec d a+ showsPrec d (Dense a _) = showsPrec d a+ showsPrec _ Zero = showString "0"++ds :: f a -> Dense f a s -> f a+ds _ (Dense _ da) = da ds z _ = z {-# INLINE ds #-} -ds' :: Num a => f a -> AD (Dense f) a -> (a, f a)-ds' _ (AD (Dense a da)) = (a, da)-ds' z (AD (Lift a)) = (a, z)-ds' z (AD Zero) = (0, z)+ds' :: Num a => f a -> Dense f a s -> (a, f a)+ds' _ (Dense a da) = (a, da)+ds' z (Lift a) = (a, z)+ds' z Zero = (0, z) {-# INLINE ds' #-} -- Bind variables and count inputs-vars :: (Traversable f, Num a) => f a -> f (AD (Dense f) a)-vars as = snd $ mapAccumL outer (0 :: Int) as- where- outer !i a = (i + 1, AD $ Dense a $ snd $ mapAccumL (inner i) 0 as)- inner !i !j _ = (j + 1, if i == j then 1 else 0)+vars :: (Traversable f, Num a) => f a -> f (Dense f a s)+vars as = snd $ mapAccumL outer (0 :: Int) as where+ outer !i a = (i + 1, Dense a $ snd $ mapAccumL (inner i) 0 as)+ inner !i !j _ = (j + 1, if i == j then 1 else 0) {-# INLINE vars #-} -apply :: (Traversable f, Num a) => (f (AD (Dense f) a) -> b) -> f a -> b+apply :: (Traversable f, Num a) => (f (Dense f a s) -> b) -> f a -> b apply f as = f (vars as) {-# INLINE apply #-} -instance Primal (Dense f) where- primal Zero = 0- primal (Lift a) = a- primal (Dense a _) = a+primal :: Num a => Dense f a s -> a+primal Zero = 0+primal (Lift a) = a+primal (Dense a _) = a -instance (Traversable f, Lifted (Dense f)) => Mode (Dense f) where- auto = Lift- zero = Zero+instance (Num a, Traversable f) => Mode (Dense f a s) where+ auto = Lift+ zero = Zero - Zero <+> a = a- a <+> Zero = a- Lift a <+> Lift b = Lift (a + b)- Lift a <+> Dense b db = Dense (a + b) db- Dense a da <+> Lift b = Dense (a + b) da- Dense a da <+> Dense b db = Dense (a + b) $ zipWithT (+) da db - 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+ _ *^ Zero = Zero+ a *^ Lift b = Lift (a * b)+ a *^ Dense b db = Dense (a * b) $ fmap (a*) db+ Zero ^* _ = Zero+ Lift a ^* b = Lift (a * b)+ Dense a da ^* b = Dense (a * b) $ fmap (*b) da+ Zero ^/ _ = Zero+ Lift a ^/ b = Lift (a / b)+ Dense a da ^/ b = Dense (a / b) $ fmap (/b) da - _ *^ Zero = Zero- a *^ Lift b = Lift (a * b)- a *^ Dense b db = Dense (a * b) $ fmap (a*) db- Zero ^* _ = Zero- Lift a ^* b = Lift (a * b)- Dense a da ^* b = Dense (a * b) $ fmap (*b) da- Zero ^/ _ = Zero- Lift a ^/ b = Lift (a / b)- Dense a da ^/ b = Dense (a / b) $ fmap (/b) da+(<+>) :: (Traversable f, Num a) => Dense f a s -> Dense f a s -> Dense f a s+Zero <+> a = a+a <+> Zero = a+Lift a <+> Lift b = Lift (a + b)+Lift a <+> Dense b db = Dense (a + b) db+Dense a da <+> Lift b = Dense (a + b) da+Dense a da <+> Dense b db = Dense (a + b) $ zipWithT (+) da db -instance (Traversable f, Lifted (Dense f)) => Jacobian (Dense f) where- type D (Dense f) = Id- unary f _ Zero = Lift (f 0)- unary f _ (Lift b) = Lift (f b)- unary f (Id dadb) (Dense b db) = Dense (f b) (fmap (dadb *) db)+(<**>) :: (Traversable f, Floating a) => Dense f a s -> Dense f a s -> Dense f a s+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 * log xi)) x y - lift1 f _ Zero = Lift (f 0)- lift1 f _ (Lift b) = Lift (f b)- lift1 f df (Dense b db) = Dense (f b) (fmap (dadb *) db)- where- Id dadb = df (Id b)+instance (Traversable f, Num a) => Jacobian (Dense f a s) where+ type D (Dense f a s) = Id a s+ unary f _ Zero = Lift (f 0)+ unary f _ (Lift b) = Lift (f b)+ unary f (Id dadb) (Dense b db) = Dense (f b) (fmap (dadb *) db) - lift1_ f _ Zero = Lift (f 0)- lift1_ f _ (Lift b) = Lift (f b)- lift1_ f df (Dense b db) = Dense a (fmap (dadb *) db)- where- a = f b- Id dadb = df (Id a) (Id b)+ lift1 f _ Zero = Lift (f 0)+ lift1 f _ (Lift b) = Lift (f b)+ lift1 f df (Dense b db) = Dense (f b) (fmap (dadb *) db) where+ Id dadb = df (Id b) - 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 (Dense c dc) = Dense (f 0 c) $ fmap (* dadc) dc- binary f _ (Id dadc) (Lift b) (Dense c dc) = Dense (f b c) $ fmap (* dadc) dc- binary f (Id dadb) _ (Dense b db) Zero = Dense (f b 0) $ fmap (dadb *) db- binary f (Id dadb) _ (Dense b db) (Lift c) = Dense (f b c) $ fmap (dadb *) db- binary f (Id dadb) (Id dadc) (Dense b db) (Dense c dc) = Dense (f b c) $ zipWithT productRule db dc- where productRule dbi dci = dadb * dbi + dci * dadc+ lift1_ f _ Zero = Lift (f 0)+ lift1_ f _ (Lift b) = Lift (f b)+ lift1_ f df (Dense b db) = Dense a (fmap (dadb *) db) where+ a = f b+ Id dadb = df (Id a) (Id b) - lift2 f _ Zero Zero = Lift (f 0 0)- lift2 f _ Zero (Lift c) = Lift (f 0 c)- lift2 f _ (Lift b) Zero = Lift (f b 0)- lift2 f _ (Lift b) (Lift c) = Lift (f b c)- lift2 f df Zero (Dense c dc) = Dense (f 0 c) $ fmap (*dadc) dc where dadc = runId (snd (df (Id 0) (Id c)))- lift2 f df (Lift b) (Dense c dc) = Dense (f b c) $ fmap (*dadc) dc where dadc = runId (snd (df (Id b) (Id c)))- lift2 f df (Dense b db) Zero = Dense (f b 0) $ fmap (dadb*) db where dadb = runId (fst (df (Id b) (Id 0)))- lift2 f df (Dense b db) (Lift c) = Dense (f b c) $ fmap (dadb*) db where dadb = runId (fst (df (Id b) (Id c)))- lift2 f df (Dense b db) (Dense c dc) = Dense (f b c) da- where- (Id dadb, Id dadc) = df (Id b) (Id c)- da = zipWithT productRule db dc- productRule dbi dci = dadb * dbi + dci * dadc+ 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 (Dense c dc) = Dense (f 0 c) $ fmap (* dadc) dc+ binary f _ (Id dadc) (Lift b) (Dense c dc) = Dense (f b c) $ fmap (* dadc) dc+ binary f (Id dadb) _ (Dense b db) Zero = Dense (f b 0) $ fmap (dadb *) db+ binary f (Id dadb) _ (Dense b db) (Lift c) = Dense (f b c) $ fmap (dadb *) db+ binary f (Id dadb) (Id dadc) (Dense b db) (Dense c dc) = Dense (f b c) $ zipWithT productRule db dc where+ productRule dbi dci = dadb * dbi + dci * dadc - lift2_ f _ Zero Zero = Lift (f 0 0)- lift2_ f _ Zero (Lift c) = Lift (f 0 c)- lift2_ f _ (Lift b) Zero = Lift (f b 0)- lift2_ f _ (Lift b) (Lift c) = Lift (f b c)- lift2_ f df Zero (Dense c dc)- = Dense a $ fmap (*dadc) dc- where- a = f 0 c- (_, Id dadc) = df (Id a) (Id 0) (Id c)- lift2_ f df (Lift b) (Dense c dc)- = Dense a $ fmap (*dadc) dc- where- a = f b c- (_, Id dadc) = df (Id a) (Id b) (Id c)- lift2_ f df (Dense b db) Zero- = Dense a $ fmap (dadb*) db- where- a = f b 0- (Id dadb, _) = df (Id a) (Id b) (Id 0)- lift2_ f df (Dense b db) (Lift c)- = Dense a $ fmap (dadb*) db- where- a = f b c- (Id dadb, _) = df (Id a) (Id b) (Id c)- lift2_ f df (Dense b db) (Dense c dc)- = Dense a $ zipWithT productRule db dc- where- a = f b c- (Id dadb, Id dadc) = df (Id a) (Id b) (Id c)- productRule dbi dci = dadb * dbi + dci * dadc+ lift2 f _ Zero Zero = Lift (f 0 0)+ lift2 f _ Zero (Lift c) = Lift (f 0 c)+ lift2 f _ (Lift b) Zero = Lift (f b 0)+ lift2 f _ (Lift b) (Lift c) = Lift (f b c)+ lift2 f df Zero (Dense c dc) = Dense (f 0 c) $ fmap (*dadc) dc where dadc = runId (snd (df (Id 0) (Id c)))+ lift2 f df (Lift b) (Dense c dc) = Dense (f b c) $ fmap (*dadc) dc where dadc = runId (snd (df (Id b) (Id c)))+ lift2 f df (Dense b db) Zero = Dense (f b 0) $ fmap (dadb*) db where dadb = runId (fst (df (Id b) (Id 0)))+ lift2 f df (Dense b db) (Lift c) = Dense (f b c) $ fmap (dadb*) db where dadb = runId (fst (df (Id b) (Id c)))+ lift2 f df (Dense b db) (Dense c dc) = Dense (f b c) da where+ (Id dadb, Id dadc) = df (Id b) (Id c)+ da = zipWithT productRule db dc+ productRule dbi dci = dadb * dbi + dci * dadc -let f = varT (mkName "f") in- deriveLifted- (classP ''Traversable [f]:)- (conT ''Dense `appT` f)+ lift2_ f _ Zero Zero = Lift (f 0 0)+ lift2_ f _ Zero (Lift c) = Lift (f 0 c)+ lift2_ f _ (Lift b) Zero = Lift (f b 0)+ lift2_ f _ (Lift b) (Lift c) = Lift (f b c)+ lift2_ f df Zero (Dense c dc) = Dense a $ fmap (*dadc) dc where+ a = f 0 c+ (_, Id dadc) = df (Id a) (Id 0) (Id c)+ lift2_ f df (Lift b) (Dense c dc) = Dense a $ fmap (*dadc) dc where+ a = f b c+ (_, Id dadc) = df (Id a) (Id b) (Id c)+ lift2_ f df (Dense b db) Zero = Dense a $ fmap (dadb*) db where+ a = f b 0+ (Id dadb, _) = df (Id a) (Id b) (Id 0)+ lift2_ f df (Dense b db) (Lift c) = Dense a $ fmap (dadb*) db where+ a = f b c+ (Id dadb, _) = df (Id a) (Id b) (Id c)+ lift2_ f df (Dense b db) (Dense c dc) = Dense a $ zipWithT productRule db dc where+ a = f b c+ (Id dadb, Id dadc) = df (Id a) (Id b) (Id c)+ productRule dbi dci = dadb * dbi + dci * dadc++#define BODY1(x) (Traversable f, x)+#define BODY2(x,y) (Traversable f, x, y)+#define HEAD Dense f a s+#include "instances.h"
src/Numeric/AD/Internal/Forward.hs view
@@ -1,9 +1,17 @@-{-# LANGUAGE Rank2Types, TypeFamilies, DeriveDataTypeable, TemplateHaskell, UndecidableInstances, BangPatterns #-}--- {-# OPTIONS_HADDOCK hide, prune #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_HADDOCK not-home #-}+ ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Internal.Forward--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -15,190 +23,188 @@ ----------------------------------------------------------------------------- module Numeric.AD.Internal.Forward- ( Forward(..)- , tangent- , bundle- , unbundle- , apply- , bind- , bind'- , bindWith- , bindWith'- , transposeWith- ) where+ ( Forward(..)+ , primal+ , tangent+ , bundle+ , unbundle+ , apply+ , bind+ , bind'+ , bindWith+ , bindWith'+ , transposeWith+ ) where -import Language.Haskell.TH-import Data.Typeable-import Data.Traversable (Traversable, mapAccumL)-import Data.Foldable (Foldable, toList)+import Control.Monad (join)+import Control.Applicative hiding ((<**>)) import Data.Data-import Control.Applicative-import Numeric.AD.Internal.Types-import Numeric.AD.Internal.Classes+import Data.Foldable (Foldable, toList)+import Data.Number.Erf+import Data.Traversable (Traversable, mapAccumL)+import Numeric.AD.Internal.Combinators import Numeric.AD.Internal.Identity+import Numeric.AD.Jacobian+import Numeric.AD.Mode +#ifdef HLINT {-# ANN module "HLint: ignore Reduce duplication" #-}+#endif -- | 'Forward' mode AD-data Forward a+data Forward a s = Forward !a a | Lift !a | Zero deriving (Show, Data, Typeable) +type instance Scalar (Forward a s) = a+ -- | Calculate the 'tangent' using forward mode AD.-tangent :: Num a => AD Forward a -> a-tangent (AD (Forward _ da)) = da+tangent :: Num a => Forward a s -> a+tangent (Forward _ da) = da tangent _ = 0 {-# INLINE tangent #-} -unbundle :: Num a => AD Forward a -> (a, a)-unbundle (AD (Forward a da)) = (a, da)-unbundle (AD Zero) = (0,0)-unbundle (AD (Lift a)) = (a, 0)+unbundle :: Num a => Forward a s -> (a, a)+unbundle (Forward a da) = (a, da)+unbundle Zero = (0,0)+unbundle (Lift a) = (a, 0) {-# INLINE unbundle #-} -bundle :: a -> a -> AD Forward a-bundle a da = AD (Forward a da)+bundle :: a -> a -> Forward a s+bundle = Forward {-# INLINE bundle #-} -apply :: Num a => (AD Forward a -> b) -> a -> b+apply :: Num a => (Forward a s -> b) -> a -> b apply f a = f (bundle a 1) {-# INLINE apply #-} -instance Primal Forward where- primal (Forward a _) = a- primal (Lift a) = a- primal Zero = 0--instance Lifted Forward => Mode Forward where- auto = Lift- zero = Zero+primal :: Num a => Forward a s -> a+primal (Forward a _) = a+primal (Lift a) = a+primal Zero = 0 - isKnownZero Zero = True- isKnownZero _ = False+instance Num a => Mode (Forward a s) where+ auto = Lift+ zero = Zero - isKnownConstant Forward{} = False- isKnownConstant _ = True+ isKnownZero Zero = True+ isKnownZero _ = False - Zero <+> a = a- a <+> Zero = a- Forward a da <+> Forward b db = Forward (a + b) (da + db)- Forward a da <+> Lift b = Forward (a + b) da- Lift a <+> Forward b db = Forward (a + b) db- Lift a <+> Lift b = Lift (a + b)+ isKnownConstant Forward{} = False+ isKnownConstant _ = True - 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+ a *^ Forward b db = Forward (a * b) (a * db)+ a *^ Lift b = Lift (a * b)+ _ *^ Zero = Zero - a *^ Forward b db = Forward (a * b) (a * db)- a *^ Lift b = Lift (a * b)- _ *^ Zero = Zero+ Forward a da ^* b = Forward (a * b) (da * b)+ Lift a ^* b = Lift (a * b)+ Zero ^* _ = Zero - Forward a da ^* b = Forward (a * b) (da * b)- Lift a ^* b = Lift (a * b)- Zero ^* _ = Zero+ Forward a da ^/ b = Forward (a / b) (da / b)+ Lift a ^/ b = Lift (a / b)+ Zero ^/ _ = Zero - Forward a da ^/ b = Forward (a / b) (da / b)- Lift a ^/ b = Lift (a / b)- Zero ^/ _ = Zero+(<+>) :: Num a => Forward a s -> Forward a s -> Forward a s+Zero <+> a = a+a <+> Zero = a+Forward a da <+> Forward b db = Forward (a + b) (da + db)+Forward a da <+> Lift b = Forward (a + b) da+Lift a <+> Forward b db = Forward (a + b) db+Lift a <+> Lift b = Lift (a + b) -instance Lifted Forward => Jacobian Forward where- type D Forward = Id+(<**>) :: Floating a => Forward a s -> Forward a s -> Forward a s+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 * log xi)) x y +instance Num a => Jacobian (Forward a s) where+ type D (Forward a s) = Id a s - unary f (Id dadb) (Forward b db) = Forward (f b) (dadb * db)- unary f _ (Lift b) = Lift (f b)- unary f _ Zero = Lift (f 0)+ unary f (Id dadb) (Forward b db) = Forward (f b) (dadb * db)+ unary f _ (Lift b) = Lift (f b)+ unary f _ Zero = Lift (f 0) - lift1 f _ Zero = Lift (f 0)- lift1 f _ (Lift b) = Lift (f b)- lift1 f df (Forward b db) = Forward (f b) (dadb * db)- where- Id dadb = df (Id b)+ lift1 f _ Zero = Lift (f 0)+ lift1 f _ (Lift b) = Lift (f b)+ lift1 f df (Forward b db) = Forward (f b) (dadb * db) where+ Id dadb = df (Id b) - lift1_ f _ Zero = Lift (f 0)- lift1_ f _ (Lift b) = Lift (f b)- lift1_ f df (Forward b db) = Forward a da- where- a = f b- Id da = df (Id a) (Id b) ^* db+ lift1_ f _ Zero = Lift (f 0)+ lift1_ f _ (Lift b) = Lift (f b)+ lift1_ f df (Forward b db) = Forward a da where+ a = f b+ Id da = df (Id a) (Id b) ^* db - 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 (Forward c dc) = Forward (f 0 c) $ dc * dadc- binary f _ (Id dadc) (Lift b) (Forward c dc) = Forward (f b c) $ dc * dadc- binary f (Id dadb) _ (Forward b db) Zero = Forward (f b 0) $ dadb * db- binary f (Id dadb) _ (Forward b db) (Lift c) = Forward (f b c) $ dadb * db- binary f (Id dadb) (Id dadc) (Forward b db) (Forward c dc) = Forward (f b c) $ dadb * db + dc * dadc+ 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 (Forward c dc) = Forward (f 0 c) $ dc * dadc+ binary f _ (Id dadc) (Lift b) (Forward c dc) = Forward (f b c) $ dc * dadc+ binary f (Id dadb) _ (Forward b db) Zero = Forward (f b 0) $ dadb * db+ binary f (Id dadb) _ (Forward b db) (Lift c) = Forward (f b c) $ dadb * db+ binary f (Id dadb) (Id dadc) (Forward b db) (Forward c dc) = Forward (f b c) $ dadb * db + dc * dadc - lift2 f _ Zero Zero = Lift (f 0 0)- lift2 f _ Zero (Lift c) = Lift (f 0 c)- lift2 f _ (Lift b) Zero = Lift (f b 0)- lift2 f _ (Lift b) (Lift c) = Lift (f b c)- lift2 f df Zero (Forward c dc) = Forward (f 0 c) $ dc * runId (snd (df (Id 0) (Id c)))- lift2 f df (Lift b) (Forward c dc) = Forward (f b c) $ dc * runId (snd (df (Id b) (Id c)))- lift2 f df (Forward b db) Zero = Forward (f b 0) $ runId (fst (df (Id b) (Id 0))) * db- lift2 f df (Forward b db) (Lift c) = Forward (f b c) $ runId (fst (df (Id b) (Id c))) * db- lift2 f df (Forward b db) (Forward c dc) = Forward a da- where- a = f b c- (Id dadb, Id dadc) = df (Id b) (Id c)- da = dadb * db + dc * dadc+ lift2 f _ Zero Zero = Lift (f 0 0)+ lift2 f _ Zero (Lift c) = Lift (f 0 c)+ lift2 f _ (Lift b) Zero = Lift (f b 0)+ lift2 f _ (Lift b) (Lift c) = Lift (f b c)+ lift2 f df Zero (Forward c dc) = Forward (f 0 c) $ dc * runId (snd (df (Id 0) (Id c)))+ lift2 f df (Lift b) (Forward c dc) = Forward (f b c) $ dc * runId (snd (df (Id b) (Id c)))+ lift2 f df (Forward b db) Zero = Forward (f b 0) $ runId (fst (df (Id b) (Id 0))) * db+ lift2 f df (Forward b db) (Lift c) = Forward (f b c) $ runId (fst (df (Id b) (Id c))) * db+ lift2 f df (Forward b db) (Forward c dc) = Forward a da where+ a = f b c+ (Id dadb, Id dadc) = df (Id b) (Id c)+ da = dadb * db + dc * dadc - lift2_ f _ Zero Zero = Lift (f 0 0)- lift2_ f _ Zero (Lift c) = Lift (f 0 c)- lift2_ f _ (Lift b) Zero = Lift (f b 0)- lift2_ f _ (Lift b) (Lift c) = Lift (f b c)- lift2_ f df Zero (Forward c dc) = Forward a $ dc * runId (snd (df (Id a) (Id 0) (Id c))) where a = f 0 c- lift2_ f df (Lift b) (Forward c dc) = Forward a $ dc * runId (snd (df (Id a) (Id b) (Id c))) where a = f b c- lift2_ f df (Forward b db) Zero = Forward a $ runId (fst (df (Id a) (Id b) (Id 0))) * db where a = f b 0- lift2_ f df (Forward b db) (Lift c) = Forward a $ runId (fst (df (Id a) (Id b) (Id c))) * db where a = f b c- lift2_ f df (Forward b db) (Forward c dc) = Forward a da- where- a = f b c- (Id dadb, Id dadc) = df (Id a) (Id b) (Id c)- da = dadb * db + dc * dadc+ lift2_ f _ Zero Zero = Lift (f 0 0)+ lift2_ f _ Zero (Lift c) = Lift (f 0 c)+ lift2_ f _ (Lift b) Zero = Lift (f b 0)+ lift2_ f _ (Lift b) (Lift c) = Lift (f b c)+ lift2_ f df Zero (Forward c dc) = Forward a $ dc * runId (snd (df (Id a) (Id 0) (Id c))) where a = f 0 c+ lift2_ f df (Lift b) (Forward c dc) = Forward a $ dc * runId (snd (df (Id a) (Id b) (Id c))) where a = f b c+ lift2_ f df (Forward b db) Zero = Forward a $ runId (fst (df (Id a) (Id b) (Id 0))) * db where a = f b 0+ lift2_ f df (Forward b db) (Lift c) = Forward a $ runId (fst (df (Id a) (Id b) (Id c))) * db where a = f b c+ lift2_ f df (Forward b db) (Forward c dc) = Forward a da where+ a = f b c+ (Id dadb, Id dadc) = df (Id a) (Id b) (Id c)+ da = dadb * db + dc * dadc -deriveLifted id $ conT ''Forward+#define HEAD Forward a s+#include "instances.h" -bind :: (Traversable f, Num a) => (f (AD Forward a) -> b) -> f a -> f b-bind f as = snd $ mapAccumL outer (0 :: Int) as- where- outer !i _ = (i + 1, f $ snd $ mapAccumL (inner i) 0 as)- inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)+bind :: (Traversable f, Num a) => (f (Forward a s) -> b) -> f a -> f b+bind f as = snd $ mapAccumL outer (0 :: Int) as where+ outer !i _ = (i + 1, f $ snd $ mapAccumL (inner i) 0 as)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a) -bind' :: (Traversable f, Num a) => (f (AD Forward a) -> b) -> f a -> (b, f b)-bind' f as = dropIx $ mapAccumL outer (0 :: Int, b0) as- where- outer (!i, _) _ = let b = f $ snd $ mapAccumL (inner i) (0 :: Int) as in ((i + 1, b), b)- inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)- b0 = f (auto <$> as)- dropIx ((_,b),bs) = (b,bs)+bind' :: (Traversable f, Num a) => (f (Forward a s) -> b) -> f a -> (b, f b)+bind' f as = dropIx $ mapAccumL outer (0 :: Int, b0) as where+ outer (!i, _) _ = let b = f $ snd $ mapAccumL (inner i) (0 :: Int) as in ((i + 1, b), b)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)+ b0 = f (auto <$> as)+ dropIx ((_,b),bs) = (b,bs) -bindWith :: (Traversable f, Num a) => (a -> b -> c) -> (f (AD Forward a) -> b) -> f a -> f c-bindWith g f as = snd $ mapAccumL outer (0 :: Int) as- where- outer !i a = (i + 1, g a $ f $ snd $ mapAccumL (inner i) 0 as)- inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)+bindWith :: (Traversable f, Num a) => (a -> b -> c) -> (f (Forward a s) -> b) -> f a -> f c+bindWith g f as = snd $ mapAccumL outer (0 :: Int) as where+ outer !i a = (i + 1, g a $ f $ snd $ mapAccumL (inner i) 0 as)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a) -bindWith' :: (Traversable f, Num a) => (a -> b -> c) -> (f (AD Forward a) -> b) -> f a -> (b, f c)-bindWith' g f as = dropIx $ mapAccumL outer (0 :: Int, b0) as- where- outer (!i, _) a = let b = f $ snd $ mapAccumL (inner i) (0 :: Int) as in ((i + 1, b), g a b)- inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)- b0 = f (auto <$> as)- dropIx ((_,b),bs) = (b,bs)+bindWith' :: (Traversable f, Num a) => (a -> b -> c) -> (f (Forward a s) -> b) -> f a -> (b, f c)+bindWith' g f as = dropIx $ mapAccumL outer (0 :: Int, b0) as where+ outer (!i, _) a = let b = f $ snd $ mapAccumL (inner i) (0 :: Int) as in ((i + 1, b), g a b)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)+ b0 = f (auto <$> as)+ dropIx ((_,b),bs) = (b,bs) -- we can't transpose arbitrary traversables, since we can't construct one out of whole cloth, and the outer -- traversable could be empty. So instead we use one as a 'skeleton' transposeWith :: (Functor f, Foldable f, Traversable g) => (b -> f a -> c) -> f (g a) -> g b -> g c-transposeWith f as = snd . mapAccumL go xss0- where- go xss b = (tail <$> xss, f b (head <$> xss))- xss0 = toList <$> as-+transposeWith f as = snd . mapAccumL go xss0 where+ go xss b = (tail <$> xss, f b (head <$> xss))+ xss0 = toList <$> as
+ src/Numeric/AD/Internal/Forward/Double.hs view
@@ -0,0 +1,227 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_HADDOCK not-home #-}++-----------------------------------------------------------------------------+---- |+---- Copyright : (c) Edward Kmett 2010-2014+---- License : BSD3+---- Maintainer : ekmett@gmail.com+---- Stability : experimental+---- Portability : GHC only+----+---- Unsafe and often partial combinators intended for internal usage.+----+---- Handle with care.+-------------------------------------------------------------------------------++module Numeric.AD.Internal.Forward.Double+ ( ForwardDouble(..)+ , bundle+ , unbundle+ , apply+ , bind+ , bind'+ , bindWith+ , bindWith'+ , transposeWith+ ) where++import Control.Applicative hiding ((<**>))+import Control.Monad (join)+import Data.Foldable (Foldable, toList)+import Data.Function (on)+import Data.Number.Erf+import Data.Traversable (Traversable, mapAccumL)+import Numeric.AD.Internal.Combinators+import Numeric.AD.Internal.Identity+import Numeric.AD.Jacobian+import Numeric.AD.Mode++data ForwardDouble a = ForwardDouble { primal, tangent :: {-# UNPACK #-} !Double }+ deriving (Read, Show)++type instance Scalar (ForwardDouble s) = Double++unbundle :: ForwardDouble s -> (Double, Double)+unbundle (ForwardDouble a da) = (a, da)+{-# INLINE unbundle #-}++bundle :: Double -> Double -> ForwardDouble s+bundle = ForwardDouble+{-# INLINE bundle #-}++apply :: (ForwardDouble s -> b) -> Double -> b+apply f a = f (bundle a 1)+{-# INLINE apply #-}++instance Mode (ForwardDouble s) where+ auto = flip ForwardDouble 0+ zero = ForwardDouble 0 0++ isKnownZero (ForwardDouble 0 0) = True+ isKnownZero _ = False++ isKnownConstant (ForwardDouble _ 0) = True+ isKnownConstant _ = False++ a *^ ForwardDouble b db = ForwardDouble (a * b) (a * db)++ ForwardDouble a da ^* b = ForwardDouble (a * b) (da * b)++ ForwardDouble a da ^/ b = ForwardDouble (a / b) (da / b)++(<+>) :: ForwardDouble s -> ForwardDouble s -> ForwardDouble s+ForwardDouble a da <+> ForwardDouble b db = ForwardDouble (a + b) (da + db)++instance Jacobian (ForwardDouble s) where+ type D (ForwardDouble s) = Id Double s++ unary f (Id dadb) (ForwardDouble b db) = ForwardDouble (f b) (dadb * db)++ lift1 f df (ForwardDouble b db) = ForwardDouble (f b) (dadb * db) where+ Id dadb = df (Id b)++ lift1_ f df (ForwardDouble b db) = ForwardDouble a da where+ a = f b+ Id da = df (Id a) (Id b) ^* db++ binary f (Id dadb) (Id dadc) (ForwardDouble b db) (ForwardDouble c dc) = ForwardDouble (f b c) $ dadb * db + dc * dadc++ lift2 f df (ForwardDouble b db) (ForwardDouble c dc) = ForwardDouble a da where+ a = f b c+ (Id dadb, Id dadc) = df (Id b) (Id c)+ da = dadb * db + dc * dadc++ lift2_ f df (ForwardDouble b db) (ForwardDouble c dc) = ForwardDouble a da where+ a = f b c+ (Id dadb, Id dadc) = df (Id a) (Id b) (Id c)+ da = dadb * db + dc * dadc++instance Eq (ForwardDouble s) where+ (==) = on (==) primal++instance Ord (ForwardDouble s) where+ compare = on compare primal++instance Num (ForwardDouble s) where+ fromInteger 0 = zero+ fromInteger n = auto (fromInteger n)+ (+) = (<+>) -- binary (+) 1 1+ (-) = binary (-) (auto 1) (auto (-1)) -- TODO: <-> ? as it is, this might be pretty bad for Tower+ (*) = lift2 (*) (\x y -> (y, x))+ negate = lift1 negate (const (auto (-1)))+ abs = lift1 abs signum+ signum a = lift1 signum (const zero) a++instance Fractional (ForwardDouble s) where+ fromRational 0 = zero+ fromRational r = auto (fromRational r)+ x / y = x * recip y+ recip = lift1_ recip (const . negate . join (*))++instance Floating (ForwardDouble s) where+ pi = auto pi+ exp = lift1_ exp const+ log = lift1 log recip+ logBase x y = log y / log x+ sqrt = lift1_ sqrt (\z _ -> recip (auto 2 * z))+ ForwardDouble 0 0 ** ForwardDouble a _ = ForwardDouble (0 ** a) 0+ _ ** ForwardDouble 0 0 = ForwardDouble 1 0+ x ** ForwardDouble y 0 = lift1 (**y) (\z -> y *^ z ** Id (y - 1)) x+ x ** y = lift2_ (**) (\z xi yi -> (yi * z / xi, z * log xi)) x y+ sin = lift1 sin cos+ cos = lift1 cos $ negate . sin+ tan = lift1 tan $ recip . join (*) . cos+ asin = lift1 asin $ \x -> recip (sqrt (auto 1 - join (*) x))+ acos = lift1 acos $ \x -> negate (recip (sqrt (1 - join (*) x)))+ atan = lift1 atan $ \x -> recip (1 + join (*) x)+ sinh = lift1 sinh cosh+ cosh = lift1 cosh sinh+ tanh = lift1 tanh $ recip . join (*) . cosh+ asinh = lift1 asinh $ \x -> recip (sqrt (1 + join (*) x))+ acosh = lift1 acosh $ \x -> recip (sqrt (join (*) x - 1))+ atanh = lift1 atanh $ \x -> recip (1 - join (*) x)++instance Enum (ForwardDouble s) where+ succ = lift1 succ (const 1)+ pred = lift1 pred (const 1)+ toEnum = auto . toEnum+ fromEnum = fromEnum . primal+ enumFrom a = withPrimal a <$> enumFrom (primal a)+ enumFromTo a b = withPrimal a <$> enumFromTo (primal a) (primal b)+ enumFromThen a b = zipWith (fromBy a delta) [0..] $ enumFromThen (primal a) (primal b) where delta = b - a+ enumFromThenTo a b c = zipWith (fromBy a delta) [0..] $ enumFromThenTo (primal a) (primal b) (primal c) where delta = b - a++instance Real (ForwardDouble s) where+ toRational = toRational . primal++instance RealFloat (ForwardDouble s) where+ floatRadix = floatRadix . primal+ floatDigits = floatDigits . primal+ floatRange = floatRange . primal+ decodeFloat = decodeFloat . primal+ encodeFloat m e = auto (encodeFloat m e)+ isNaN = isNaN . primal+ isInfinite = isInfinite . primal+ isDenormalized = isDenormalized . primal+ isNegativeZero = isNegativeZero . primal+ isIEEE = isIEEE . primal+ exponent = exponent+ scaleFloat n = unary (scaleFloat n) (scaleFloat n 1)+ significand x = unary significand (scaleFloat (- floatDigits x) 1) x+ atan2 = lift2 atan2 $ \vx vy -> let r = recip (join (*) vx + join (*) vy) in (vy * r, negate vx * r)++instance RealFrac (ForwardDouble s) where+ properFraction a = (w, a `withPrimal` pb) where+ pa = primal a+ (w, pb) = properFraction pa+ truncate = truncate . primal+ round = round . primal+ ceiling = ceiling . primal+ floor = floor . primal++instance Erf (ForwardDouble s) where+ erf = lift1 erf $ \x -> (2 / sqrt pi) * exp (negate x * x)+ erfc = lift1 erfc $ \x -> ((-2) / sqrt pi) * exp (negate x * x)+ normcdf = lift1 normcdf $ \x -> ((-1) / sqrt pi) * exp (x * x * fromRational (- recip 2) / sqrt 2)++instance InvErf (ForwardDouble s) where+ inverf = lift1 inverfc $ \x -> recip $ (2 / sqrt pi) * exp (negate x * x)+ inverfc = lift1 inverfc $ \x -> recip $ negate (2 / sqrt pi) * exp (negate x * x)+ invnormcdf = lift1 invnormcdf $ \x -> recip $ ((-1) / sqrt pi) * exp (x * x * fromRational (- recip 2) / sqrt 2)++bind :: (Traversable f) => (f (ForwardDouble s) -> b) -> f Double -> f b+bind f as = snd $ mapAccumL outer (0 :: Int) as where+ outer !i _ = (i + 1, f $ snd $ mapAccumL (inner i) 0 as)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)++bind' :: (Traversable f) => (f (ForwardDouble s) -> b) -> f Double -> (b, f b)+bind' f as = dropIx $ mapAccumL outer (0 :: Int, b0) as where+ outer (!i, _) _ = let b = f $ snd $ mapAccumL (inner i) (0 :: Int) as in ((i + 1, b), b)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)+ b0 = f (auto <$> as)+ dropIx ((_,b),bs) = (b,bs)++bindWith :: (Traversable f) => (Double -> b -> c) -> (f (ForwardDouble s) -> b) -> f Double -> f c+bindWith g f as = snd $ mapAccumL outer (0 :: Int) as where+ outer !i a = (i + 1, g a $ f $ snd $ mapAccumL (inner i) 0 as)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)++bindWith' :: (Traversable f) => (Double -> b -> c) -> (f (ForwardDouble s) -> b) -> f Double -> (b, f c)+bindWith' g f as = dropIx $ mapAccumL outer (0 :: Int, b0) as where+ outer (!i, _) a = let b = f $ snd $ mapAccumL (inner i) (0 :: Int) as in ((i + 1, b), g a b)+ inner !i !j a = (j + 1, if i == j then bundle a 1 else auto a)+ b0 = f (auto <$> as)+ dropIx ((_,b),bs) = (b,bs)++transposeWith :: (Functor f, Foldable f, Traversable g) => (b -> f a -> c) -> f (g a) -> g b -> g c+transposeWith f as = snd . mapAccumL go xss0 where+ go xss b = (tail <$> xss, f b (head <$> xss))+ xss0 = toList <$> as
src/Numeric/AD/Internal/Identity.hs view
@@ -1,9 +1,14 @@-{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, DeriveDataTypeable #-}-{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS_HADDOCK not-home #-}+ ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Internal.Identity--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -11,146 +16,43 @@ -- ----------------------------------------------------------------------------- module Numeric.AD.Internal.Identity- ( Id(..)- , probe- , unprobe- , probed- , unprobed- ) where+ ( Id(..)+ , probe+ , unprobe+ , probed+ , unprobed+ ) where -import Control.Applicative import Data.Data (Data)-import Data.Foldable (Foldable, foldMap) import Data.Monoid import Data.Number.Erf import Data.Typeable (Typeable)-import Data.Traversable (Traversable, traverse)-import Numeric.AD.Internal.Classes-import Numeric.AD.Internal.Types--newtype Id a = Id { runId :: a } deriving- (Iso a, Eq, Ord, Show, Enum, Bounded, Num, Real, Fractional, Floating, RealFrac, RealFloat, Monoid, Data, Typeable)--probe :: a -> AD Id a-probe a = AD (Id a)--unprobe :: AD Id a -> a-unprobe (AD (Id a)) = a--pid :: f a -> f (Id a)-pid = iso--unpid :: f (Id a) -> f a-unpid = osi--probed :: f a -> f (AD Id a)-probed = iso . pid--unprobed :: f (AD Id a) -> f a-unprobed = unpid . osi--instance Functor Id where- fmap f (Id a) = Id (f a)+import Numeric.AD.Mode -instance Foldable Id where- foldMap f (Id a) = f a+newtype Id a s = Id { runId :: a } deriving+ (Eq, Ord, Show, Enum, Bounded, Num, Real, Fractional, Floating, RealFrac, RealFloat, Monoid, Data, Typeable, Erf, InvErf) -instance Traversable Id where- traverse f (Id a) = Id <$> f a+type instance Scalar (Id a s) = a -instance Applicative Id where- pure = Id- Id f <*> Id a = Id (f a)+probe :: a -> Id a s+probe = Id -instance Monad Id where- return = Id- Id a >>= f = f a+unprobe :: Id a s -> a+unprobe = runId -instance Lifted Id where- (==!) = (==)- compare1 = compare- showsPrec1 = showsPrec- fromInteger1 = fromInteger- (+!) = (+)- (-!) = (-)- (*!) = (*)- negate1 = negate- abs1 = abs- signum1 = signum- (/!) = (/)- recip1 = recip- fromRational1 = fromRational- toRational1 = toRational- pi1 = pi- exp1 = exp- log1 = log- sqrt1 = sqrt- (**!) = (**)- logBase1 = logBase- sin1 = sin- cos1 = cos- tan1 = tan- asin1 = asin- acos1 = acos- atan1 = atan- sinh1 = sinh- cosh1 = cosh- tanh1 = tanh- asinh1 = asinh- acosh1 = acosh- atanh1 = atanh- properFraction1 = properFraction- truncate1 = truncate- round1 = round- ceiling1 = ceiling- floor1 = floor- floatRadix1 = floatRadix- floatDigits1 = floatDigits- floatRange1 = floatRange- decodeFloat1 = decodeFloat- encodeFloat1 = encodeFloat- exponent1 = exponent- significand1 = significand- scaleFloat1 = scaleFloat- isNaN1 = isNaN- isInfinite1 = isInfinite- isDenormalized1 = isDenormalized- isNegativeZero1 = isNegativeZero- isIEEE1 = isIEEE- atan21 = atan2- succ1 = succ- pred1 = pred- toEnum1 = toEnum- fromEnum1 = fromEnum- enumFrom1 = enumFrom- enumFromThen1 = enumFromThen- enumFromTo1 = enumFromTo- enumFromThenTo1 = enumFromThenTo- minBound1 = minBound- maxBound1 = maxBound- erf1 = erf- erfc1 = erfc- normcdf1 = normcdf- inverf1 = inverf- inverfc1 = inverfc- invnormcdf1 = invnormcdf+pid :: Functor f => f a -> f (Id a s)+pid = fmap probe -instance Mode Id where- auto = Id- Id a ^* b = Id (a * b)- a *^ Id b = Id (a * b)- Id a <+> Id b = Id (a + b)- Id a <**> Id b = Id (a ** b)+unpid :: Functor f => f (Id a s) -> f a+unpid = fmap unprobe -instance Primal Id where- primal (Id a) = a+probed :: Functor f => f a -> f (Id a s)+probed = pid -instance Erf a => Erf (Id a) where- erf = Id . erf . runId- erfc = Id . erfc . runId- normcdf = Id . normcdf . runId+unprobed :: Functor f => f (Id a s) -> f a+unprobed = unpid -instance InvErf a => InvErf (Id a) where- inverf = Id . inverf . runId- inverfc = Id . inverfc . runId- invnormcdf = Id . invnormcdf . runId+instance Num a => Mode (Id a s) where+ auto = Id+ Id a ^* b = Id (a * b)+ a *^ Id b = Id (a * b)
− src/Numeric/AD/Internal/Jet.hs
@@ -1,97 +0,0 @@-{-# LANGUAGE CPP, TypeOperators, ScopedTypeVariables, FlexibleContexts #-}-{-# OPTIONS_HADDOCK hide #-}--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Internal.Jet--- Copyright : (c) Edward Kmett 2010--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : GHC only----------------------------------------------------------------------------------module Numeric.AD.Internal.Jet- ( Jet(..)- , headJet- , tailJet- , jet- ) where--#ifndef MIN_VERSION_base-#define MIN_VERSION_base(x,y,z) 1-#endif--import Control.Applicative-import Data.Foldable-import Data.Traversable-import Data.Monoid-#if MIN_VERSION_base(4,4,0)-import Data.Typeable (Typeable1(..), TyCon, mkTyCon3, mkTyConApp)-#else-import Data.Typeable (Typeable1(..), TyCon, mkTyCon, mkTyConApp)-#endif-import Control.Comonad.Cofree--infixl 3 :----- | A 'Jet' is a tower of all (higher order) partial derivatives of a function------ At each step, a @'Jet' f@ is wrapped in another layer worth of @f@.------ > a :- f a :- f (f a) :- f (f (f a)) :- ...-data Jet f a = a :- Jet f (f a)---- | Used to sidestep the need for UndecidableInstances.-newtype Showable = Showable (Int -> String -> String)--instance Show Showable where- showsPrec d (Showable f) = f d--showable :: Show a => a -> Showable-showable a = Showable (`showsPrec` a)---- Polymorphic recursion precludes 'Data' in its current form, as no Data1 class exists--- Polymorphic recursion also breaks 'show' for 'Jet'!--- factor Show1 out of Lifted?-instance (Functor f, Show (f Showable), Show a) => Show (Jet f a) where- showsPrec d (a :- as) = showParen (d > 3) $- showsPrec 4 a . showString " :- " . showsPrec 3 (fmap showable <$> as)--instance Functor f => Functor (Jet f) where- fmap f (a :- as) = f a :- fmap (fmap f) as--instance Foldable f => Foldable (Jet f) where- foldMap f (a :- as) = f a `mappend` foldMap (foldMap f) as--instance Traversable f => Traversable (Jet f) where- traverse f (a :- as) = (:-) <$> f a <*> traverse (traverse f) as---- | Take the tail of a 'Jet'.-tailJet :: Jet f a -> Jet f (f a)-tailJet (_ :- as) = as-{-# INLINE tailJet #-}---- | Take the head of a 'Jet'.-headJet :: Jet f a -> a-headJet (a :- _) = a-{-# INLINE headJet #-}---- | Construct a 'Jet' by unzipping the layers of a 'Cofree' 'Comonad'.-jet :: Functor f => Cofree f a -> Jet f a-jet (a :< as) = a :- dist (jet <$> as)- where- dist :: Functor f => f (Jet f a) -> Jet f (f a)- dist x = (headJet <$> x) :- dist (tailJet <$> x)--instance Typeable1 f => Typeable1 (Jet f) where- typeOf1 tfa = mkTyConApp jetTyCon [typeOf1 (undefined `asArgsType` tfa)]- where asArgsType :: f a -> t f a -> f a- asArgsType = const--jetTyCon :: TyCon-#if MIN_VERSION_base(4,4,0)-jetTyCon = mkTyCon3 "ad" "Numeric.AD.Internal.Jet" "Jet"-#else-jetTyCon = mkTyCon "Numeric.AD.Internal.Jet.Jet"-#endif-{-# NOINLINE jetTyCon #-}
src/Numeric/AD/Internal/Kahn.hs view
@@ -1,10 +1,18 @@-{-# LANGUAGE Rank2Types, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, ScopedTypeVariables, TemplateHaskell, TypeFamilies, DeriveDataTypeable, FunctionalDependencies #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-full-laziness #-}+{-# OPTIONS_HADDOCK not-home #-} --- {-# OPTIONS_HADDOCK hide, prune #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Internal.Kahn--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -21,128 +29,143 @@ ----------------------------------------------------------------------------- module Numeric.AD.Internal.Kahn- ( Kahn(..)- , Tape(..)- , partials- , partialArray- , partialMap- , derivative- , derivative'- , vgrad, vgrad'- , Grad(..)- ) where+ ( Kahn(..)+ , Tape(..)+ , partials+ , partialArray+ , partialMap+ , derivative+ , derivative'+ , vgrad, vgrad'+ , Grad(..)+ , bind+ , unbind+ , unbindMap+ , unbindWith+ , unbindMapWithDefault+ , primal+ , var+ , varId+ ) where import Prelude hiding (mapM) import Control.Applicative (Applicative(..),(<$>)) import Control.Monad.ST-import Control.Monad (forM_)+import Control.Monad hiding (mapM)+import Control.Monad.Trans.State import Data.List (foldl') import Data.Array.ST import Data.Array-import Data.IntMap (IntMap, fromListWith)+import Data.IntMap (IntMap, fromListWith, findWithDefault) import Data.Graph (Vertex, transposeG, Graph)+import Data.Number.Erf 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.Traversable (Traversable, mapM) import Data.Typeable (Typeable)-import Numeric.AD.Internal.Types-import Numeric.AD.Internal.Classes+import Numeric.AD.Internal.Combinators import Numeric.AD.Internal.Identity-import Numeric.AD.Internal.Var+import Numeric.AD.Jacobian+import Numeric.AD.Mode -- | 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)+ = 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)+newtype Kahn a s = Kahn (Tape a (Kahn a s)) deriving (Show, Typeable) +type instance Scalar (Kahn a s) = a+ -- deriving instance (Data (Tape a (Kahn a)) => Data (Kahn a) -instance MuRef (Kahn a) where- type DeRef (Kahn a) = Tape a+instance MuRef (Kahn a s) where+ type DeRef (Kahn a s) = 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+ 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+instance Num a => Mode (Kahn a s) where+ isKnownZero (Kahn Zero) = True+ isKnownZero _ = False - isKnownConstant (Kahn Zero) = True- isKnownConstant (Kahn (Lift _)) = True- isKnownConstant _ = 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+ auto a = Kahn (Lift a)+ zero = Kahn Zero+ 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+(<+>) :: Num a => Kahn a s -> Kahn a s -> Kahn a s+(<+>) = binary (+) 1 1 -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+(<**>) :: Floating a => Kahn a s -> Kahn a s -> Kahn a s+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 * xi)) x y -instance Lifted Kahn => Jacobian Kahn where- type D Kahn = Id+primal :: Num a => Kahn a s -> a+primal (Kahn Zero) = 0+primal (Kahn (Lift a)) = a+primal (Kahn (Var a _)) = a+primal (Kahn (Binary a _ _ _ _)) = a+primal (Kahn (Unary a _ _)) = a - 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)+instance Num a => Jacobian (Kahn a s) where+ type D (Kahn a s) = Id a s - lift1 f df b = unary f (df (Id pb)) b- where pb = primal b+ 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 (const a) (df (Id a) (Id pb)) b- where pb = primal b- a = f pb+ lift1 f df b = unary f (df (Id pb)) b where+ pb = primal b - 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)+ lift1_ f df b = unary (const a) (df (Id a) (Id pb)) b where+ pb = primal b+ a = f pb - lift2 f df b c = binary f dadb dadc b c- where (dadb, dadc) = df (Id (primal b)) (Id (primal c))+ 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 (\_ _ -> 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)+ lift2 f df b c = binary f dadb dadc b c where+ (dadb, dadc) = df (Id (primal b)) (Id (primal c)) -deriveLifted id (conT ''Kahn)+ 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) -derivative :: Num a => AD Kahn a -> a+#define HEAD Kahn a s+#include <instances.h>++derivative :: Num a => Kahn a s -> a derivative = sum . map snd . partials {-# INLINE derivative #-} -derivative' :: Num a => AD Kahn a -> (a, a)+derivative' :: Num a => Kahn a s -> (a, a) derivative' r = (primal r, derivative r) {-# INLINE derivative' #-} @@ -150,15 +173,15 @@ backPropagate :: Num a => (Vertex -> (Tape a Int, Int, [Int])) -> STArray s Int a -> Vertex -> ST s () backPropagate vmap ss v = case node of Unary _ g b -> do- da <- readArray ss i- db <- readArray ss b- writeArray ss b (db + g*da)+ 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)+ 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@@ -166,99 +189,106 @@ 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- return $ if b then m : ms' else 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 []+ 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+ return $ if b then m : ms' else 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+{-# SPECIALIZE partials :: Kahn Double s -> [(Int, Double)] #-}+partials :: forall s a . Num a => Kahn a s -> [(Int, a)]+partials 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+ 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!+ 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 _ = []+ 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 :: Num a => (Int, Int) -> Kahn a s -> 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 :: Num a => Kahn a s -> 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'+ pack :: i -> [Kahn a ()] -> 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 Num a => Grad (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:))+instance Grad i o o' a => Grad (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 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+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 +var :: a -> Int -> Kahn a s+var a v = Kahn (Var a v)++varId :: Kahn a s -> Int+varId (Kahn (Var _ v)) = v+varId _ = error "varId: not a Var"++bind :: Traversable f => f a -> (f (Kahn a s), (Int,Int))+bind xs = (r,(0,hi)) where+ (r,hi) = runState (mapM freshVar xs) 0+ freshVar a = state $ \s -> let s' = s + 1 in s' `seq` (var a s, s')++unbind :: Functor f => f (Kahn a s) -> Array Int a -> f a+unbind xs ys = fmap (\v -> ys ! varId v) xs++unbindWith :: (Functor f, Num a) => (a -> b -> c) -> f (Kahn a s) -> Array Int b -> f c+unbindWith f xs ys = fmap (\v -> f (primal v) (ys ! varId v)) xs++unbindMap :: (Functor f, Num a) => f (Kahn a s) -> IntMap a -> f a+unbindMap xs ys = fmap (\v -> findWithDefault 0 (varId v) ys) xs++unbindMapWithDefault :: (Functor f, Num a) => b -> (a -> b -> c) -> f (Kahn a s) -> IntMap b -> f c+unbindMapWithDefault z f xs ys = fmap (\v -> f (primal v) $ findWithDefault z (varId v) ys) xs
+ src/Numeric/AD/Internal/On.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# OPTIONS_HADDOCK not-home #-}++-----------------------------------------------------------------------------+-- |+-- Copyright : (c) Edward Kmett 2010-2014+-- License : BSD3+-- Maintainer : ekmett@gmail.com+-- Stability : experimental+-- Portability : GHC only+--+-----------------------------------------------------------------------------++module Numeric.AD.Internal.On+ ( On(..)+ ) where++import Data.Number.Erf+import Data.Data+import Numeric.AD.Mode++#ifdef HLINT+#endif++------------------------------------------------------------------------------+-- On+------------------------------------------------------------------------------++-- | The composition of two AD modes is an AD mode in its own right+newtype On t = On { off :: t } deriving+ ( Eq, Enum, Ord, Bounded+ , Num, Real, Fractional+ , RealFrac, Floating, Erf+ , InvErf, RealFloat, Typeable+ )++type instance Scalar (On t) = Scalar (Scalar t)++instance (Mode t, Mode (Scalar t)) => Mode (On t) where+ auto = On . auto . auto+ a *^ On b = On (auto a *^ b)+ On a ^* b = On (a ^* auto b)
src/Numeric/AD/Internal/Reverse.hs view
@@ -1,9 +1,20 @@-{-# LANGUAGE CPP, Rank2Types, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances, ScopedTypeVariables, TemplateHaskell, GADTs, TypeFamilies, DeriveDataTypeable, FlexibleContexts #-}--- {-# OPTIONS_HADDOCK hide, prune #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-full-laziness #-}+{-# OPTIONS_HADDOCK not-home #-}+ ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Internal.Reverse--- Copyright : (c) Edward Kmett 2012+-- Copyright : (c) Edward Kmett 2012-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -21,37 +32,51 @@ ----------------------------------------------------------------------------- module Numeric.AD.Internal.Reverse- ( Reverse(..)- , Tape(..)- , Head(..)- , Cells(..)- , reifyTape- , partials- , partialArrayOf- , partialMapOf- , derivativeOf- , derivativeOf'- ) where+ ( Reverse(..)+ , Tape(..)+ , Head(..)+ , Cells(..)+ , reifyTape+ , partials+ , partialArrayOf+ , partialMapOf+ , derivativeOf+ , derivativeOf'+ , bind+ , unbind+ , unbindMap+ , unbindWith+ , unbindMapWithDefault+ , var+ , varId+ , primal+ ) where +import Data.Functor+import Control.Monad hiding (mapM) import Control.Monad.ST+import Control.Monad.Trans.State import Data.Array.ST import Data.Array import Data.Array.Unsafe as Unsafe import Data.IORef-import Data.IntMap (IntMap, fromDistinctAscList)+import Data.IntMap (IntMap, fromDistinctAscList, findWithDefault)+import Data.Number.Erf import Data.Proxy import Data.Reflection+import Data.Traversable (Traversable, mapM) import Data.Typeable-import Language.Haskell.TH hiding (reify)-import Numeric.AD.Internal.Types-import Numeric.AD.Internal.Classes+import Numeric.AD.Internal.Combinators import Numeric.AD.Internal.Identity-import Numeric.AD.Internal.Var+import Numeric.AD.Jacobian+import Numeric.AD.Mode import Prelude hiding (mapM) import System.IO.Unsafe (unsafePerformIO) import Unsafe.Coerce +#ifdef HLINT {-# ANN module "HLint: ignore Reduce duplication" #-}+#endif -- evil untyped tape #ifndef HLINT@@ -89,25 +114,27 @@ -- | 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 :: forall s a. Reifies s Tape => (a -> a) -> a -> Int -> a -> Reverse a s unarily f di i b = Reverse (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 -> Reverse s a+binarily :: forall s a. Reifies s Tape => (a -> a -> a) -> a -> a -> Int -> a -> Int -> a -> Reverse a s binarily f di dj i b j c = Reverse (unsafePerformIO (modifyTape (Proxy :: Proxy s) (bin i j di dj))) $! f b c {-# INLINE binarily #-} #ifndef HLINT-data Reverse s a where- Zero :: Reverse s a- Lift :: a -> Reverse s a- Reverse :: {-# UNPACK #-} !Int -> a -> Reverse s a+data Reverse a s where+ Zero :: Reverse a s+ Lift :: a -> Reverse a s+ Reverse :: {-# UNPACK #-} !Int -> a -> Reverse a s deriving (Show, Typeable) #endif -instance (Reifies s Tape, Lifted (Reverse s)) => Mode (Reverse s) where+type instance Scalar (Reverse a s) = a++instance (Num a, Reifies s Tape) => Mode (Reverse a s) where isKnownZero Zero = True isKnownZero _ = False @@ -116,66 +143,70 @@ 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+(<+>) :: (Reifies s Tape, Num a) => Reverse a s -> Reverse a s -> Reverse a s+(<+>) = binary (+) 1 1 -instance Primal (Reverse s) where- primal Zero = 0- primal (Lift a) = a- primal (Reverse _ a) = a+(<**>) :: (Reifies s Tape, Floating a) => Reverse a s -> Reverse a s -> Reverse a s+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 * log xi)) x y -instance (Reifies s Tape, Lifted (Reverse s)) => Jacobian (Reverse s) where- type D (Reverse s) = Id+primal :: Num a => Reverse a s -> a+primal Zero = 0+primal (Lift a) = a+primal (Reverse _ a) = a - 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+instance (Reifies s Tape, Num a) => Jacobian (Reverse a s) where+ type D (Reverse a s) = Id a s - lift1 f df b = unary f (df (Id pb)) b- where pb = primal b+ 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 (const a) (df (Id a) (Id pb)) b- where pb = primal b- a = f pb+ lift1 f df b = unary f (df (Id pb)) b where+ pb = primal b - 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)+ lift1_ f df b = unary (const a) (df (Id a) (Id pb)) b where+ pb = primal b+ a = f pb - 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+ 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) - lift2 f df b c = binary f dadb dadc b c- where (dadb, dadc) = df (Id (primal b)) (Id (primal 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 (\_ _ -> 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)+ lift2 f df b c = binary f dadb dadc b c where+ (dadb, dadc) = df (Id (primal b)) (Id (primal c)) -let s = varT (mkName "s") in- deriveLifted (classP ''Reifies [s, conT ''Tape] :) (conT ''Reverse `appT` s)+ 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) --- | 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+#define BODY1(x) (Reifies s Tape,x)+#define BODY2(x,y) (Reifies s Tape,x,y)+#define HEAD Reverse a s+#include "instances.h"++-- | Helper that extracts the derivative of a chain when the chain was constructed with 1 variable.+derivativeOf :: (Reifies s Tape, Num a) => Proxy s -> Reverse a s -> 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 (Reverse s) a -> (a, a)+-- | Helper that extracts both the primal and derivative of a chain when the chain was constructed with 1 variable.+derivativeOf' :: (Reifies s Tape, Num a) => Proxy s -> Reverse a s -> (a, a) derivativeOf' p r = (primal r, derivativeOf p r) {-# INLINE derivativeOf' #-} @@ -196,27 +227,27 @@ (backPropagate $! k - 1) xs ss -- | 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)+{-# SPECIALIZE partials :: Reifies s Tape => Reverse Double s -> [Double] #-}+partials :: forall s a. (Reifies s Tape, Num a) => Reverse a s -> [a]+partials Zero = []+partials (Lift _) = []+partials (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.-partialArrayOf :: (Reifies s Tape, Num a) => Proxy s -> (Int, Int) -> AD (Reverse s) a -> Array Int a+partialArrayOf :: (Reifies s Tape, Num a) => Proxy s -> (Int, Int) -> Reverse a s -> 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 (Reverse s) a -> IntMap a+partialMapOf :: (Reifies s Tape, Num a) => Proxy s -> Reverse a s -> IntMap a partialMapOf _ = fromDistinctAscList . zip [0..] . partials {-# INLINE partialMapOf #-} @@ -227,7 +258,26 @@ return (reify (Tape h) k) {-# NOINLINE reifyTape #-} -instance Var (Reverse s) where- var a v = Reverse v a- varId (Reverse v _) = v- varId _ = error "varId: not a Var"+var :: a -> Int -> Reverse a s+var a v = Reverse v a++varId :: Reverse a s -> Int+varId (Reverse v _) = v+varId _ = error "varId: not a Var"++bind :: Traversable f => f a -> (f (Reverse a s), (Int,Int))+bind xs = (r,(0,hi)) where+ (r,hi) = runState (mapM freshVar xs) 0+ freshVar a = state $ \s -> let s' = s + 1 in s' `seq` (var a s, s')++unbind :: Functor f => f (Reverse a s) -> Array Int a -> f a+unbind xs ys = fmap (\v -> ys ! varId v) xs++unbindWith :: (Functor f, Num a) => (a -> b -> c) -> f (Reverse a s) -> Array Int b -> f c+unbindWith f xs ys = fmap (\v -> f (primal v) (ys ! varId v)) xs++unbindMap :: (Functor f, Num a) => f (Reverse a s) -> IntMap a -> f a+unbindMap xs ys = fmap (\v -> findWithDefault 0 (varId v) ys) xs++unbindMapWithDefault :: (Functor f, Num a) => b -> (a -> b -> c) -> f (Reverse a s) -> IntMap b -> f c+unbindMapWithDefault z f xs ys = fmap (\v -> f (primal v) $ findWithDefault z (varId v) ys) xs
src/Numeric/AD/Internal/Sparse.hs view
@@ -1,35 +1,48 @@-{-# LANGUAGE BangPatterns, TemplateHaskell, TypeFamilies, TypeOperators, FlexibleContexts, UndecidableInstances, DeriveDataTypeable, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-}+{-# OPTIONS_HADDOCK not-home #-}+ module Numeric.AD.Internal.Sparse- ( Index(..)- , emptyIndex- , addToIndex- , indices- , Sparse(..)- , apply- , vars- , d, d', ds- , skeleton- , spartial- , partial- , vgrad- , vgrad'- , vgrads- , Grad(..)- , Grads(..)- ) where+ ( Index(..)+ , emptyIndex+ , addToIndex+ , indices+ , Sparse(..)+ , apply+ , vars+ , d, d', ds+ , skeleton+ , spartial+ , partial+ , vgrad+ , vgrad'+ , vgrads+ , Grad(..)+ , Grads(..)+ ) where import Prelude hiding (lookup) import Control.Applicative hiding ((<**>))-import Numeric.AD.Internal.Classes import Control.Comonad.Cofree-import Numeric.AD.Internal.Types+import Control.Monad (join) import Data.Data-import Data.Typeable ()-import qualified Data.IntMap as IntMap import Data.IntMap (IntMap, mapWithKey, unionWith, findWithDefault, toAscList, singleton, insertWith, lookup)+import qualified Data.IntMap as IntMap+import Data.Number.Erf import Data.Traversable-import Language.Haskell.TH+import Data.Typeable ()+import Numeric.AD.Internal.Combinators+import Numeric.AD.Jacobian+import Numeric.AD.Mode newtype Index = Index (IntMap Int) @@ -50,32 +63,33 @@ -- which it was found. This should be key for efficiently computing sparse hessians. -- there are only (n + k - 1) choose k distinct nth partial derivatives of a -- function with k inputs.-data Sparse a- = Sparse !a (IntMap (Sparse a))+data Sparse a s+ = Sparse !a (IntMap (Sparse a s)) | Zero deriving (Show, Data, Typeable) +type instance Scalar (Sparse a s) = a+ -- | drop keys below a given value dropMap :: Int -> IntMap a -> IntMap a dropMap n = snd . IntMap.split (n - 1) {-# INLINE dropMap #-} -times :: Num a => Sparse a -> Int -> Sparse a -> Sparse a+times :: Num a => Sparse a s -> Int -> Sparse a s -> Sparse a s times Zero _ _ = Zero times _ _ Zero = Zero times (Sparse a as) n (Sparse b bs) = Sparse (a * b) $- unionWith (<+>)- (fmap (^* b) (dropMap n as))- (fmap (a *^) (dropMap n bs))+ unionWith (+)+ (fmap (^* b) (dropMap n as))+ (fmap (a *^) (dropMap n bs)) {-# INLINE times #-} -vars :: (Traversable f, Num a) => f a -> f (AD Sparse a)-vars = snd . mapAccumL var 0- where- var !n a = (n + 1, AD $ Sparse a $ singleton n $ auto 1)+vars :: (Traversable f, Num a) => f a -> f (Sparse a s)+vars = snd . mapAccumL var 0 where+ var !n a = (n + 1, Sparse a $ singleton n $ auto 1) {-# INLINE vars #-} -apply :: (Traversable f, Num a) => (f (AD Sparse a) -> b) -> f a -> b+apply :: (Traversable f, Num a) => (f (Sparse a s) -> b) -> f a -> b apply f = f . vars {-# INLINE apply #-} @@ -83,24 +97,23 @@ skeleton = snd . mapAccumL (\ !n _ -> (n + 1, n)) 0 {-# INLINE skeleton #-} -d :: (Traversable f, Num a) => f b -> AD Sparse a -> f a-d fs (AD Zero) = 0 <$ fs-d fs (AD (Sparse _ da)) = snd $ mapAccumL (\ !n _ -> (n + 1, maybe 0 primal $ lookup n da)) 0 fs+d :: (Traversable f, Num a) => f b -> Sparse a s -> f a+d fs (Zero) = 0 <$ fs+d fs (Sparse _ da) = snd $ mapAccumL (\ !n _ -> (n + 1, maybe 0 primal $ lookup n da)) 0 fs {-# INLINE d #-} -d' :: (Traversable f, Num a) => f a -> AD Sparse a -> (a, f a)-d' fs (AD Zero) = (0, 0 <$ fs)-d' fs (AD (Sparse a da)) = (a, snd $ mapAccumL (\ !n _ -> (n + 1, maybe 0 primal $ lookup n da)) 0 fs)+d' :: (Traversable f, Num a) => f a -> Sparse a s -> (a, f a)+d' fs Zero = (0, 0 <$ fs)+d' fs (Sparse a da) = (a, snd $ mapAccumL (\ !n _ -> (n + 1, maybe 0 primal $ lookup n da)) 0 fs) {-# INLINE d' #-} -ds :: (Traversable f, Num a) => f b -> AD Sparse a -> Cofree f a-ds fs (AD Zero) = r where r = 0 :< (r <$ fs)-ds fs (AD as@(Sparse a _)) = a :< (go emptyIndex <$> fns)- where- fns = skeleton fs- -- go :: Index -> Int -> Cofree f a- go ix i = partial (indices ix') as :< (go ix' <$> fns)- where ix' = addToIndex i ix+ds :: (Traversable f, Num a) => f b -> Sparse a s -> Cofree f a+ds fs Zero = r where r = 0 :< (r <$ fs)+ds fs (as@(Sparse a _)) = a :< (go emptyIndex <$> fns) where+ fns = skeleton fs+ -- go :: Index -> Int -> Cofree f a+ go ix i = partial (indices ix') as :< (go ix' <$> fns) where+ ix' = addToIndex i ix {-# INLINE ds #-} {-@@ -129,128 +142,131 @@ {-# INLINE vds #-} -} -partial :: Num a => [Int] -> Sparse a -> a+partial :: Num a => [Int] -> Sparse a s -> a partial [] (Sparse a _) = a partial (n:ns) (Sparse _ da) = partial ns $ findWithDefault (auto 0) n da partial _ Zero = 0 {-# INLINE partial #-} -spartial :: Num a => [Int] -> Sparse a -> Maybe a+spartial :: Num a => [Int] -> Sparse a s -> Maybe a spartial [] (Sparse a _) = Just a spartial (n:ns) (Sparse _ da) = do- a' <- lookup n da- spartial ns a'+ a' <- lookup n da+ spartial ns a' spartial _ Zero = Nothing {-# INLINE spartial #-} -instance Primal Sparse where- primal (Sparse a _) = a- primal Zero = 0+primal :: Num a => Sparse a s -> a+primal (Sparse a _) = a+primal Zero = 0 -instance Lifted Sparse => Mode Sparse where- auto a = Sparse a IntMap.empty- zero = Zero- Zero <**> y = auto (0 ** primal y)- _ <**> Zero = auto 1- x <**> y@(Sparse b bs)- | IntMap.null bs = lift1 (**b) (\z -> b *^ z <**> Sparse (b-1) IntMap.empty) x- | otherwise = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y- Zero <+> a = a- a <+> Zero = a- Sparse a as <+> Sparse b bs = Sparse (a + b) $ unionWith (<+>) as bs- Zero ^* _ = Zero- Sparse a as ^* b = Sparse (a * b) $ fmap (^* b) as- _ *^ Zero = Zero- a *^ Sparse b bs = Sparse (a * b) $ fmap (a *^) bs- Zero ^/ _ = Zero- Sparse a as ^/ b = Sparse (a / b) $ fmap (^/ b) as+(<**>) :: Floating a => Sparse a s -> Sparse a s -> Sparse a s+Zero <**> y = auto (0 ** primal y)+_ <**> Zero = auto 1+x <**> y@(Sparse b bs)+ | IntMap.null bs = lift1 (**b) (\z -> b *^ z <**> Sparse (b-1) IntMap.empty) x+ | otherwise = lift2_ (**) (\z xi yi -> (yi * z / xi, z * log xi)) x y -instance Lifted Sparse => Jacobian Sparse where- type D Sparse = Sparse- unary f _ Zero = auto (f 0)- unary f dadb (Sparse pb bs) = Sparse (f pb) $ mapWithKey (times dadb) bs+instance Num a => Mode (Sparse a s) where+ auto a = Sparse a IntMap.empty+ zero = Zero - lift1 f _ Zero = auto (f 0)- lift1 f df b@(Sparse pb bs) = Sparse (f pb) $ mapWithKey (times (df b)) bs+ Zero ^* _ = Zero+ Sparse a as ^* b = Sparse (a * b) $ fmap (^* b) as+ _ *^ Zero = Zero+ a *^ Sparse b bs = Sparse (a * b) $ fmap (a *^) bs+ Zero ^/ _ = Zero+ Sparse a as ^/ b = Sparse (a / b) $ fmap (^/ b) as - lift1_ f _ Zero = auto (f 0)- lift1_ f df b@(Sparse pb bs) = a where- a = Sparse (f pb) $ mapWithKey (times (df a b)) bs+infixr 6 <+> - binary f _ _ Zero Zero = auto (f 0 0)- binary f _ dadc Zero (Sparse pc dc) = Sparse (f 0 pc) $ mapWithKey (times dadc) dc- binary f dadb _ (Sparse pb db) Zero = Sparse (f pb 0 ) $ mapWithKey (times dadb) db- binary f dadb dadc (Sparse pb db) (Sparse pc dc) = Sparse (f pb pc) $- unionWith (<+>)- (mapWithKey (times dadb) db)- (mapWithKey (times dadc) dc)+(<+>) :: Num a => Sparse a s -> Sparse a s -> Sparse a s+Zero <+> a = a+a <+> Zero = a+Sparse a as <+> Sparse b bs = Sparse (a + b) $ unionWith (<+>) as bs - lift2 f _ Zero Zero = auto (f 0 0)- lift2 f df Zero c@(Sparse pc dc) = Sparse (f 0 pc) $ mapWithKey (times dadc) dc where dadc = snd (df zero c)- lift2 f df b@(Sparse pb db) Zero = Sparse (f pb 0) $ mapWithKey (times dadb) db where dadb = fst (df b zero)- lift2 f df b@(Sparse pb db) c@(Sparse pc dc) = Sparse (f pb pc) da where- (dadb, dadc) = df b c- da = unionWith (<+>)- (mapWithKey (times dadb) db)- (mapWithKey (times dadc) dc)+instance Num a => Jacobian (Sparse a s) where+ type D (Sparse a s) = Sparse a s+ unary f _ Zero = auto (f 0)+ unary f dadb (Sparse pb bs) = Sparse (f pb) $ mapWithKey (times dadb) bs - lift2_ f _ Zero Zero = auto (f 0 0)- lift2_ f df b@(Sparse pb db) Zero = a where a = Sparse (f pb 0) (mapWithKey (times (fst (df a b zero))) db)- lift2_ f df Zero c@(Sparse pc dc) = a where a = Sparse (f 0 pc) (mapWithKey (times (snd (df a zero c))) dc)- lift2_ f df b@(Sparse pb db) c@(Sparse pc dc) = a where- (dadb, dadc) = df a b c- a = Sparse (f pb pc) da- da = unionWith (<+>)- (mapWithKey (times dadb) db)- (mapWithKey (times dadc) dc)+ lift1 f _ Zero = auto (f 0)+ lift1 f df b@(Sparse pb bs) = Sparse (f pb) $ mapWithKey (times (df b)) bs -deriveLifted id $ conT ''Sparse+ lift1_ f _ Zero = auto (f 0)+ lift1_ f df b@(Sparse pb bs) = a where+ a = Sparse (f pb) $ mapWithKey (times (df a b)) bs + binary f _ _ Zero Zero = auto (f 0 0)+ binary f _ dadc Zero (Sparse pc dc) = Sparse (f 0 pc) $ mapWithKey (times dadc) dc+ binary f dadb _ (Sparse pb db) Zero = Sparse (f pb 0 ) $ mapWithKey (times dadb) db+ binary f dadb dadc (Sparse pb db) (Sparse pc dc) = Sparse (f pb pc) $+ unionWith (<+>)+ (mapWithKey (times dadb) db)+ (mapWithKey (times dadc) dc) + lift2 f _ Zero Zero = auto (f 0 0)+ lift2 f df Zero c@(Sparse pc dc) = Sparse (f 0 pc) $ mapWithKey (times dadc) dc where dadc = snd (df zero c)+ lift2 f df b@(Sparse pb db) Zero = Sparse (f pb 0) $ mapWithKey (times dadb) db where dadb = fst (df b zero)+ lift2 f df b@(Sparse pb db) c@(Sparse pc dc) = Sparse (f pb pc) da where+ (dadb, dadc) = df b c+ da = unionWith (<+>)+ (mapWithKey (times dadb) db)+ (mapWithKey (times dadc) dc)++ lift2_ f _ Zero Zero = auto (f 0 0)+ lift2_ f df b@(Sparse pb db) Zero = a where a = Sparse (f pb 0) (mapWithKey (times (fst (df a b zero))) db)+ lift2_ f df Zero c@(Sparse pc dc) = a where a = Sparse (f 0 pc) (mapWithKey (times (snd (df a zero c))) dc)+ lift2_ f df b@(Sparse pb db) c@(Sparse pc dc) = a where+ (dadb, dadc) = df a b c+ a = Sparse (f pb pc) da+ da = unionWith (<+>)+ (mapWithKey (times dadb) db)+ (mapWithKey (times dadc) dc)++#define HEAD Sparse a s+#include "instances.h"+ class Num a => Grad i o o' a | i -> a o o', o -> a i o', o' -> a i o where- pack :: i -> [AD Sparse a] -> AD Sparse a- unpack :: ([a] -> [a]) -> o- unpack' :: ([a] -> (a, [a])) -> o'+ pack :: i -> [Sparse a ()] -> Sparse a ()+ unpack :: ([a] -> [a]) -> o+ unpack' :: ([a] -> (a, [a])) -> o' -instance Num a => Grad (AD Sparse a) [a] (a, [a]) a where- pack i _ = i- unpack f = f []- unpack' f = f []+instance Num a => Grad (Sparse a ()) [a] (a, [a]) a where+ pack i _ = i+ unpack f = f []+ unpack' f = f [] -instance Grad i o o' a => Grad (AD Sparse 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:))+instance Grad i o o' a => Grad (Sparse 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 = d as $ apply f as+vgrad i = unpack (unsafeGrad (pack i)) where+ unsafeGrad f as = d as $ apply f as {-# INLINE vgrad #-} vgrad' :: Grad i o o' a => i -> o'-vgrad' i = unpack' (unsafeGrad' (pack i))- where- unsafeGrad' f as = d' as $ apply f as+vgrad' i = unpack' (unsafeGrad' (pack i)) where+ unsafeGrad' f as = d' as $ apply f as {-# INLINE vgrad' #-} class Num a => Grads i o a | i -> a o, o -> a i where- packs :: i -> [AD Sparse a] -> AD Sparse a- unpacks :: ([a] -> Cofree [] a) -> o+ packs :: i -> [Sparse a ()] -> Sparse a ()+ unpacks :: ([a] -> Cofree [] a) -> o -instance Num a => Grads (AD Sparse a) (Cofree [] a) a where- packs i _ = i- unpacks f = f []+instance Num a => Grads (Sparse a ()) (Cofree [] a) a where+ packs i _ = i+ unpacks f = f [] -instance Grads i o a => Grads (AD Sparse a -> i) (a -> o) a where- packs f (a:as) = packs (f a) as- packs _ [] = error "Grad.pack: logic error"- unpacks f a = unpacks (f . (a:))+instance Grads i o a => Grads (Sparse a () -> i) (a -> o) a where+ packs f (a:as) = packs (f a) as+ packs _ [] = error "Grad.pack: logic error"+ unpacks f a = unpacks (f . (a:)) vgrads :: Grads i o a => i -> o-vgrads i = unpacks (unsafeGrads (packs i))- where- unsafeGrads f as = ds as $ apply f as+vgrads i = unpacks (unsafeGrads (packs i)) where+ unsafeGrads f as = ds as $ apply f as {-# INLINE vgrads #-}-
src/Numeric/AD/Internal/Tower.hs view
@@ -1,10 +1,18 @@-{-# LANGUAGE Rank2Types, TypeFamilies, FlexibleContexts, UndecidableInstances, TemplateHaskell, DeriveDataTypeable #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-}--- {-# OPTIONS_HADDOCK hide, prune #-}+{-# OPTIONS_HADDOCK not-home #-}+ ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Tower.Internal--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -13,35 +21,39 @@ ----------------------------------------------------------------------------- module Numeric.AD.Internal.Tower- ( Tower(..)- , zeroPad- , zeroPadF- , transposePadF- , d- , d'- , withD- , tangents- , bundle- , apply- , getADTower- , tower- ) where+ ( Tower(..)+ , zeroPad+ , zeroPadF+ , transposePadF+ , d+ , d'+ , withD+ , tangents+ , bundle+ , apply+ , getADTower+ , tower+ ) where import Prelude hiding (all) import Control.Applicative hiding ((<**>))+import Control.Monad (join) import Data.Foldable import Data.Data (Data)+import Data.Number.Erf import Data.Typeable (Typeable)-import Language.Haskell.TH-import Numeric.AD.Internal.Types-import Numeric.AD.Internal.Classes+import Numeric.AD.Internal.Combinators+import Numeric.AD.Jacobian+import Numeric.AD.Mode -- | @Tower@ is an AD 'Mode' that calculates a tangent tower by forward AD, and provides fast 'diffsUU', 'diffsUF'-newtype Tower a = Tower { getTower :: [a] } deriving (Data, Typeable)+newtype Tower a s = Tower { getTower :: [a] } deriving (Data, Typeable) -instance Show a => Show (Tower a) where- showsPrec n (Tower as) = showParen (n > 10) $ showString "Tower " . showList as+type instance Scalar (Tower a s) = a +instance Show a => Show (Tower a s) where+ showsPrec n (Tower as) = showParen (n > 10) $ showString "Tower " . showList as+ -- Local combinators zeroPad :: Num a => [a] -> [a]@@ -55,13 +67,13 @@ transposePadF :: (Foldable f, Functor f) => a -> f [a] -> [f a] transposePadF pad fx- | all null fx = []- | otherwise = fmap headPad fx : transposePadF pad (drop1 <$> fx)- where- headPad [] = pad- headPad (x:_) = x- drop1 (_:xs) = xs- drop1 xs = xs+ | all null fx = []+ | otherwise = fmap headPad fx : transposePadF pad (drop1 <$> fx)+ where+ headPad [] = pad+ headPad (x:_) = x+ drop1 (_:xs) = xs+ drop1 xs = xs d :: Num a => [a] -> a d (_:da:_) = da@@ -74,67 +86,84 @@ d' _ = (0, 0) {-# INLINE d' #-} -tangents :: Tower a -> Tower a+tangents :: Tower a s -> Tower a s tangents (Tower []) = Tower [] tangents (Tower (_:xs)) = Tower xs {-# INLINE tangents #-} -bundle :: a -> Tower a -> Tower a+truncated :: Tower a s -> Bool+truncated (Tower []) = True+truncated _ = False+{-# INLINE truncated #-}++bundle :: a -> Tower a s -> Tower a s bundle a (Tower as) = Tower (a:as) {-# INLINE bundle #-} -withD :: (a, a) -> AD Tower a-withD (a, da) = AD (Tower [a,da])+withD :: (a, a) -> Tower a s+withD (a, da) = Tower [a,da] {-# INLINE withD #-} -apply :: Num a => (AD Tower a -> b) -> a -> b-apply f a = f (AD (Tower [a,1]))+apply :: Num a => (Tower a s -> b) -> a -> b+apply f a = f (Tower [a,1]) {-# INLINE apply #-} -getADTower :: AD Tower a -> [a]-getADTower (AD t) = getTower t+getADTower :: Tower a s -> [a]+getADTower = getTower {-# INLINE getADTower #-} -tower :: [a] -> AD Tower a-tower as = AD (Tower as)+tower :: [a] -> Tower a s+tower = Tower -instance Primal Tower where- primal (Tower (x:_)) = x- primal _ = 0+primal :: Num a => Tower a s -> a+primal (Tower (x:_)) = x+primal _ = 0 -instance Lifted Tower => Mode Tower where- auto a = Tower [a]- zero = Tower []- Tower [] <**> y = auto (0 ** primal y)- _ <**> Tower [] = auto 1- x <**> Tower [y] = lift1 (**y) (\z -> y *^ z <**> Tower [y-1]) x- x <**> y = lift2_ (**) (\z xi yi -> (yi *! z /! xi, z *! log1 xi)) x y+instance Num a => Mode (Tower a s) where+ auto a = Tower [a]+ zero = Tower [] - Tower [] <+> bs = bs- as <+> Tower [] = as- Tower (a:as) <+> Tower (b:bs) = Tower (c:cs)- where- c = a + b- Tower cs = Tower as <+> Tower bs+ a *^ Tower bs = Tower (map (a*) bs)+ Tower as ^* b = Tower (map (*b) as)+ Tower as ^/ b = Tower (map (/b) as) - a *^ Tower bs = Tower (map (a*) bs)- Tower as ^* b = Tower (map (*b) as)- Tower as ^/ b = Tower (map (/b) as)+infixr 6 <+> -instance Lifted Tower => Jacobian Tower where- type D Tower = Tower- unary f dadb b = bundle (f (primal b)) (tangents b *! dadb)- lift1 f df b = bundle (f (primal b)) (tangents b *! df b)- lift1_ f df b = a where- a = bundle (f (primal b)) (tangents b *! df a b)+(<+>) :: forall a s. Num a => Tower a s -> Tower a s -> Tower a s+Tower [] <+> bs = bs+as <+> Tower [] = as+Tower (a:as) <+> Tower (b:bs) = Tower (c:cs) where+ c = a + b+ Tower cs = Tower as <+> (Tower bs :: Tower a s) - binary f dadb dadc b c = bundle (f (primal b) (primal c)) (tangents b *! dadb +! tangents c *! dadc)- lift2 f df b c = bundle (f (primal b) (primal c)) (tangents b *! dadb +! tangents c *! dadc) where- (dadb, dadc) = df b c- lift2_ f df b c = a where- a0 = f (primal b) (primal c)- da = tangents b *! dadb +! tangents c *! dadc- a = bundle a0 da- (dadb, dadc) = df a b c+instance Num a => Jacobian (Tower a s) where+ type D (Tower a s) = Tower a s+ unary f dadb b = bundle (f (primal b)) (tangents b * dadb)+ lift1 f df b = bundle (f (primal b)) (tangents b * df b)+ lift1_ f df b = a where+ a = bundle (f (primal b)) (tangents b * df a b) -deriveLifted id (conT ''Tower)+ binary f dadb dadc b c = bundle (f (primal b) (primal c)) (tangents b * dadb + tangents c * dadc)+ lift2 f df b c = bundle (f (primal b) (primal c)) tana where+ (dadb, dadc) = df b c+ tanb = tangents b+ tanc = tangents c+ tana = case (truncated tanb, truncated tanc) of+ (False, False) -> tanb * dadb + tanc * dadc+ (True, False) -> tanc * dadc+ (False, True) -> tanb * dadb+ (True, True) -> zero+ lift2_ f df b c = a where+ a0 = f (primal b) (primal c)+ da = tangents b * dadb + tangents c * dadc+ a = bundle a0 da+ (dadb, dadc) = df a b c++(<**>) :: Floating a => Tower a s -> Tower a s -> Tower a s+Tower [] <**> y = auto (0 ** primal y)+_ <**> Tower [] = auto 1+x <**> Tower [y] = lift1 (**y) (\z -> y *^ z <**> Tower [y-1]) x+x <**> y = lift2_ (**) (\z xi yi -> (yi * z / xi, z * log xi)) x y++#define HEAD Tower a s+#include <instances.h>
− src/Numeric/AD/Internal/Types.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE Rank2Types, GeneralizedNewtypeDeriving, TemplateHaskell, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances #-}-{-# OPTIONS_HADDOCK hide #-}--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Internal.Types--- Copyright : (c) Edward Kmett 2010--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : GHC only----------------------------------------------------------------------------------module Numeric.AD.Internal.Types- ( AD(..)- ) where--#ifndef MIN_VERSION_base-#define MIN_VERSION_base (x,y,z) 1-#endif--import Data.Data (Data(..), mkDataType, DataType, mkConstr, Constr, constrIndex, Fixity(..))-#if MIN_VERSION_base(4,4,0)-import Data.Typeable (Typeable1(..), Typeable(..), TyCon, mkTyCon3, mkTyConApp, gcast1)-#else-import Data.Typeable (Typeable1(..), Typeable(..), TyCon, mkTyCon, mkTyConApp, gcast1)-#endif-import Language.Haskell.TH-import Numeric.AD.Internal.Classes--{-# ANN module "HLint: ignore Eta reduce" #-}---- | 'AD' serves as a common wrapper for different 'Mode' instances, exposing a traditional--- numerical tower. Universal quantification is used to limit the actions in user code to--- machinery that will return the same answers under all AD modes, allowing us to use modes--- interchangeably as both the type level \"brand\" and dictionary, providing a common API.-newtype AD f a = AD { runAD :: f a } deriving (Iso (f a), Lifted, Mode, Primal)---- > instance (Lifted f, Num a) => Num (AD f a)--- etc.-let f = varT (mkName "f") in- deriveNumeric- (classP ''Lifted [f]:)- (conT ''AD `appT` f)--instance Typeable1 f => Typeable1 (AD f) where- typeOf1 tfa = mkTyConApp adTyCon [typeOf1 (undefined `asArgsType` tfa)]- where asArgsType :: f a -> t f a -> f a- asArgsType = const--adTyCon :: TyCon-#if MIN_VERSION_base(4,4,0)-adTyCon = mkTyCon3 "ad" "Numeric.AD.Internal.Types" "AD"-#else-adTyCon = mkTyCon "Numeric.AD.Internal.Types.AD"-#endif-{-# NOINLINE adTyCon #-}--adConstr :: Constr-adConstr = mkConstr adDataType "AD" [] Prefix-{-# NOINLINE adConstr #-}--adDataType :: DataType-adDataType = mkDataType "Numeric.AD.Internal.Types.AD" [adConstr]-{-# NOINLINE adDataType #-}--instance (Typeable1 f, Typeable a, Data (f a), Data a) => Data (AD f a) where- gfoldl f z (AD a) = z AD `f` a- toConstr _ = adConstr- gunfold k z c = case constrIndex c of- 1 -> k (z AD)- _ -> error "gunfold"- dataTypeOf _ = adDataType- dataCast1 f = gcast1 f
− src/Numeric/AD/Internal/Var.hs
@@ -1,74 +0,0 @@--- {-# OPTIONS_HADDOCK hide, prune #-}--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Internal.Var--- Copyright : (c) Edward Kmett 2012--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : GHC only------ Variables used for reverse-mode automatic differentiation.--------------------------------------------------------------------------------module Numeric.AD.Internal.Var- ( Var(..)- , bind- , unbind- , unbindMap- , unbindWith- , unbindMapWithDefault- , Variable(..)- , vary- ) where--import Prelude hiding (mapM)-import Data.Array-import Data.IntMap (IntMap, findWithDefault)-import Data.Traversable (Traversable, mapM)-import Numeric.AD.Internal.Types-import Numeric.AD.Internal.Classes---- | Used to mark variables for inspection during the reverse pass-class Primal v => Var v where- var :: a -> Int -> v a- varId :: v a -> Int--instance Var f => Var (AD f) where- var a v = AD (var a v)- varId (AD v) = varId v---- 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')--bind :: (Traversable f, Var v) => f a -> (f (v a), (Int,Int))-bind xs = (r,(0,hi)) where- (r,hi) = runS (mapM freshVar xs) 0- freshVar a = S (\s -> let s' = s + 1 in s' `seq` (var a s, s'))--unbind :: (Functor f, Var v) => f (v a) -> Array Int a -> f a-unbind xs ys = fmap (\v -> ys ! varId v) xs--unbindWith :: (Functor f, Var v, Num a) => (a -> b -> c) -> f (v a) -> Array Int b -> f c-unbindWith f xs ys = fmap (\v -> f (primal v) (ys ! varId v)) xs--unbindMap :: (Functor f, Var v, Num a) => f (v a) -> IntMap a -> f a-unbindMap xs ys = fmap (\v -> findWithDefault 0 (varId v) ys) xs--unbindMapWithDefault :: (Functor f, Var v, Num a) => b -> (a -> b -> c) -> f (v a) -> IntMap b -> f c-unbindMapWithDefault z f xs ys = fmap (\v -> f (primal v) $ findWithDefault z (varId v) ys) xs--data Variable a = Variable a {-# UNPACK #-} !Int--instance Var Variable where- var = Variable- varId (Variable _ i) = i--instance Primal Variable where- primal (Variable a _) = a--vary :: Var f => Variable a -> f a-vary (Variable a i) = var a i
+ src/Numeric/AD/Jacobian.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) Edward Kmett 2010-2014+-- License : BSD3+-- Maintainer : ekmett@gmail.com+-- Stability : experimental+-- Portability : GHC only+--+-----------------------------------------------------------------------------++module Numeric.AD.Jacobian+ ( Jacobian(..)+ ) where++import Numeric.AD.Mode++-- | 'Jacobian' is useful for defining new AD primitives in a+-- fairly generic way.+class (Mode t, Mode (D t), Num (D t)) => Jacobian t where+ type D t :: *++ unary :: (Scalar t -> Scalar t) -> D t -> t -> t+ lift1 :: (Scalar t -> Scalar t) -> (D t -> D t) -> t -> t+ lift1_ :: (Scalar t -> Scalar t) -> (D t -> D t -> D t) -> t -> t++ binary :: (Scalar t -> Scalar t -> Scalar t) -> D t -> D t -> t -> t -> t+ lift2 :: (Scalar t -> Scalar t -> Scalar t) -> (D t -> D t -> (D t, D t)) -> t -> t -> t+ lift2_ :: (Scalar t -> Scalar t -> Scalar t) -> (D t -> D t -> D t -> (D t, D t)) -> t -> t -> t
+ src/Numeric/AD/Jet.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+#if __GLASGOW_HASKELL__ >= 707+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE StandaloneDeriving #-}+#endif+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) Edward Kmett 2010-2014+-- License : BSD3+-- Maintainer : ekmett@gmail.com+-- Stability : experimental+-- Portability : GHC only+--+-----------------------------------------------------------------------------+module Numeric.AD.Jet+ ( Jet(..)+ , headJet+ , tailJet+ , jet+ ) where++#ifndef MIN_VERSION_base+#define MIN_VERSION_base(x,y,z) 1+#endif++import Control.Applicative+import Data.Foldable+import Data.Traversable+import Data.Monoid+import Data.Typeable+import Control.Comonad.Cofree++infixl 3 :-++-- | A 'Jet' is a tower of all (higher order) partial derivatives of a function+--+-- At each step, a @'Jet' f@ is wrapped in another layer worth of @f@.+--+-- > a :- f a :- f (f a) :- f (f (f a)) :- ...+data Jet f a = a :- Jet f (f a)++-- | Used to sidestep the need for UndecidableInstances.+newtype Showable = Showable (Int -> String -> String)++instance Show Showable where+ showsPrec d (Showable f) = f d++showable :: Show a => a -> Showable+showable a = Showable (`showsPrec` a)++-- Polymorphic recursion precludes 'Data' in its current form, as no Data1 class exists+-- Polymorphic recursion also breaks 'show' for 'Jet'!+-- factor Show1 out of Lifted?+instance (Functor f, Show (f Showable), Show a) => Show (Jet f a) where+ showsPrec d (a :- as) = showParen (d > 3) $+ showsPrec 4 a . showString " :- " . showsPrec 3 (fmap showable <$> as)++instance Functor f => Functor (Jet f) where+ fmap f (a :- as) = f a :- fmap (fmap f) as++instance Foldable f => Foldable (Jet f) where+ foldMap f (a :- as) = f a `mappend` foldMap (foldMap f) as++instance Traversable f => Traversable (Jet f) where+ traverse f (a :- as) = (:-) <$> f a <*> traverse (traverse f) as++-- | Take the tail of a 'Jet'.+tailJet :: Jet f a -> Jet f (f a)+tailJet (_ :- as) = as+{-# INLINE tailJet #-}++-- | Take the head of a 'Jet'.+headJet :: Jet f a -> a+headJet (a :- _) = a+{-# INLINE headJet #-}++-- | Construct a 'Jet' by unzipping the layers of a 'Cofree' 'Comonad'.+jet :: Functor f => Cofree f a -> Jet f a+jet (a :< as) = a :- dist (jet <$> as) where+ dist :: Functor f => f (Jet f a) -> Jet f (f a)+ dist x = (headJet <$> x) :- dist (tailJet <$> x)++#if __GLASGOW_HASKELL__ >= 707+deriving instance Typeable Jet+#else+instance Typeable1 f => Typeable1 (Jet f) where+ typeOf1 tfa = mkTyConApp jetTyCon [typeOf1 (undefined `asArgsType` tfa)] where+ asArgsType :: f a -> t f a -> f a+ asArgsType = const++jetTyCon :: TyCon+#if MIN_VERSION_base(4,4,0)+jetTyCon = mkTyCon3 "ad" "Numeric.AD.Internal.Jet" "Jet"+#else+jetTyCon = mkTyCon "Numeric.AD.Internal.Jet.Jet"+#endif+{-# NOINLINE jetTyCon #-}+#endif
+ src/Numeric/AD/Mode.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PatternGuards #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) Edward Kmett 2010-2014+-- License : BSD3+-- Maintainer : ekmett@gmail.com+-- Stability : experimental+-- Portability : GHC only+--+-----------------------------------------------------------------------------++module Numeric.AD.Mode+ (+ -- * AD modes+ Mode(..)+ , Scalar+ ) where++type family Scalar (t :: *) :: *++infixr 7 *^+infixl 7 ^*+infixr 7 ^/++class (Num t, Num (Scalar t)) => Mode t where+ -- | allowed to return False for items with a zero derivative, but we'll give more NaNs than strictly necessary+ isKnownConstant :: t -> Bool+ isKnownConstant _ = False++ -- | allowed to return False for zero, but we give more NaN's than strictly necessary then+ isKnownZero :: t -> Bool+ isKnownZero _ = False++ -- | Embed a constant+ auto :: Scalar t -> t++ -- | Scalar-vector multiplication+ (*^) :: Scalar t -> t -> t+ a *^ b = auto a * b++ -- | Vector-scalar multiplication+ (^*) :: t -> Scalar t -> t+ a ^* b = a * auto b++ -- | Scalar division+ (^/) :: Fractional (Scalar t) => t -> Scalar t -> t+ a ^/ b = a ^* recip b++ -- |+ -- @'zero' = 'lift' 0@+ zero :: t+ zero = auto 0
src/Numeric/AD/Mode/Directed.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE RankNTypes #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Mode.Directed--- Copyright : (c) Edward Kmett 2010-12+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -13,22 +12,22 @@ ----------------------------------------------------------------------------- module Numeric.AD.Mode.Directed- (- -- * Gradients- grad- , grad'- -- * Jacobians- , jacobian- , jacobian'- -- * Derivatives- , diff- , diff'- -- * Exposed Types- , Direction(..)- ) where+ (+ -- * Gradients+ grad+ , grad'+ -- * Jacobians+ , jacobian+ , jacobian'+ -- * Derivatives+ , diff+ , diff'+ -- * Exposed Types+ , Direction(..)+ ) where import Prelude hiding (reverse)-import Numeric.AD.Types+import Numeric.AD.Mode import Data.Traversable (Traversable) import qualified Numeric.AD.Mode.Kahn as K import qualified Numeric.AD.Mode.Forward as F@@ -38,57 +37,57 @@ import Data.Ix data Direction- = Forward- | Kahn- | Reverse- | Tower- | Mixed- deriving (Show, Eq, Ord, Read, Bounded, Enum, Ix)+ = Forward+ | Kahn+ | Reverse+ | 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 Tower = T.diff-diff Mixed = F.diff+diff :: Num a => Direction -> (forall t. Mode t => t -> t) -> a -> a+diff Forward f a = F.diff f a+diff Kahn f a = K.diff f a+diff Reverse f a = R.diff f a+diff Tower f a = T.diff f a+diff Mixed f a = F.diff f a {-# 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' Tower = T.diff'-diff' Mixed = F.diff'+diff' :: Num a => Direction -> (forall t. Mode t => t -> t) -> a -> (a, a)+diff' Forward f a = F.diff' f a+diff' Kahn f a = K.diff' f a+diff' Reverse f a = R.diff' f a+diff' Tower f a = T.diff' f a+diff' Mixed f a = F.diff' f a {-# 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 Tower = F.jacobian -- error "jacobian Tower: unimplemented"-jacobian Mixed = M.jacobian+jacobian :: (Traversable f, Traversable g, Num a) => Direction -> (forall t. Mode t => f (t) -> g (t)) -> f a -> g (f a)+jacobian Forward f a = F.jacobian f a+jacobian Kahn f a = K.jacobian f a+jacobian Reverse f a = R.jacobian f a+jacobian Tower f a = F.jacobian f a -- error "jacobian Tower: unimplemented"+jacobian Mixed f a = M.jacobian f a {-# 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' Tower = F.jacobian' -- error "jacobian' Tower: unimplemented"-jacobian' Mixed = M.jacobian'+jacobian' :: (Traversable f, Traversable g, Num a) => Direction -> (forall t. Mode t => f (t) -> g (t)) -> f a -> g (a, f a)+jacobian' Forward f a = F.jacobian' f a+jacobian' Kahn f a = K.jacobian' f a+jacobian' Reverse f a = R.jacobian' f a+jacobian' Tower f a = F.jacobian' f a -- error "jacobian' Tower: unimplemented"+jacobian' Mixed f a = M.jacobian' f a {-# 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 Tower = F.grad -- error "grad Tower: unimplemented"-grad Mixed = M.grad+grad :: (Traversable f, Num a) => Direction -> (forall t. Mode t => f (t) -> t) -> f a -> f a+grad Forward f a = F.grad f a+grad Kahn f a = K.grad f a+grad Reverse f a = R.grad f a+grad Tower f a = F.grad f a -- error "grad Tower: unimplemented"+grad Mixed f a = M.grad f a {-# 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' Tower = F.grad' -- error "grad' Tower: unimplemented"-grad' Mixed = M.grad'+grad' :: (Traversable f, Num a) => Direction -> (forall t. Mode t => f (t) -> t) -> f a -> (a, f a)+grad' Forward f a = F.grad' f a+grad' Kahn f a = K.grad' f a+grad' Reverse f a = R.grad' f a+grad' Tower f a = F.grad' f a -- error "grad' Tower: unimplemented"+grad' Mixed f a = M.grad' f a {-# INLINE grad' #-}
src/Numeric/AD/Mode/Forward.hs view
@@ -1,8 +1,8 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE Rank2Types #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Mode.Forward--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -13,59 +13,57 @@ ----------------------------------------------------------------------------- module Numeric.AD.Mode.Forward- (- -- * Gradient- grad- , grad'- , gradWith- , gradWith'- -- * Jacobian- , jacobian- , jacobian'- , jacobianWith- , jacobianWith'- -- * Transposed Jacobian- , jacobianT- , jacobianWithT- -- * Hessian Product- , hessianProduct- , hessianProduct'- -- * Derivatives- , diff- , diff'- , diffF- , diffF'- -- * Directional Derivatives- , du- , du'- , duF- , duF'- ) where+ ( Forward+ -- * Gradient+ , grad+ , grad'+ , gradWith+ , gradWith'+ -- * Jacobian+ , jacobian+ , jacobian'+ , jacobianWith+ , jacobianWith'+ -- * Transposed Jacobian+ , jacobianT+ , jacobianWithT+ -- * Hessian Product+ , hessianProduct+ , hessianProduct'+ -- * Derivatives+ , diff+ , diff'+ , diffF+ , diffF'+ -- * Directional Derivatives+ , du+ , du'+ , duF+ , duF'+ ) where import Data.Traversable (Traversable) import Control.Applicative-import Numeric.AD.Types-import Numeric.AD.Internal.Classes-import Numeric.AD.Internal.Composition import Numeric.AD.Internal.Forward+import Numeric.AD.Internal.On -- | Compute the directional derivative of a function given a zipped up 'Functor' of the input values and their derivatives-du :: (Functor f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> a+du :: (Functor f, Num a) => (forall s. f (Forward a s) -> Forward a s) -> f (a, a) -> a du f = tangent . f . fmap (uncurry bundle) {-# INLINE du #-} -- | Compute the answer and directional derivative of a function given a zipped up 'Functor' of the input values and their derivatives-du' :: (Functor f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> (a, a)+du' :: (Functor f, Num a) => (forall s. f (Forward a s) -> Forward a s) -> f (a, a) -> (a, a) du' f = unbundle . f . fmap (uncurry bundle) {-# INLINE du' #-} -- | Compute a vector of directional derivatives for a function given a zipped up 'Functor' of the input values and their derivatives.-duF :: (Functor f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f (a, a) -> g a+duF :: (Functor f, Functor g, Num a) => (forall s. f (Forward a s) -> g (Forward a s)) -> f (a, a) -> g a duF f = fmap tangent . f . fmap (uncurry bundle) {-# INLINE duF #-} -- | Compute a vector of answers and directional derivatives for a function given a zipped up 'Functor' of the input values and their derivatives.-duF' :: (Functor f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f (a, a) -> g (a, a)+duF' :: (Functor f, Functor g, Num a) => (forall s. f (Forward a s) -> g (Forward a s)) -> f (a, a) -> g (a, a) duF' f = fmap unbundle . f . fmap (uncurry bundle) {-# INLINE duF' #-} @@ -73,7 +71,7 @@ -- -- >>> diff sin 0 -- 1.0-diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a+diff :: Num a => (forall s. Forward a s -> Forward a s) -> a -> a diff f a = tangent $ apply f a {-# INLINE diff #-} @@ -90,7 +88,7 @@ -- >>> diff' exp 0 -- (1.0,1.0) -diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)+diff' :: Num a => (forall s. Forward a s -> Forward a s) -> a -> (a, a) diff' f a = unbundle $ apply f a {-# INLINE diff' #-} @@ -98,7 +96,7 @@ -- -- >>> 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 :: (Functor f, Num a) => (forall s. Forward a s -> f (Forward a s)) -> a -> f a diffF f a = tangent <$> apply f a {-# INLINE diffF #-} @@ -106,78 +104,75 @@ -- -- >>> 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' :: (Functor f, Num a) => (forall s. Forward a s -> f (Forward a s)) -> a -> f (a, a) diffF' f a = unbundle <$> apply f a {-# INLINE diffF' #-} -- | A fast, simple, transposed Jacobian computed with forward-mode AD.-jacobianT :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> f (g a)+jacobianT :: (Traversable f, Functor g, Num a) => (forall s. f (Forward a s) -> g (Forward a s)) -> f a -> f (g a) jacobianT f = bind (fmap tangent . f) {-# INLINE jacobianT #-} -- | A fast, simple, transposed Jacobian computed with 'Forward' mode 'AD' that combines the output with the input.-jacobianWithT :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> f (g b)-jacobianWithT g f = bindWith g' f- where g' a ga = g a . tangent <$> ga+jacobianWithT :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. f (Forward a s) -> g (Forward a s)) -> f a -> f (g b)+jacobianWithT g f = bindWith g' f where+ g' a ga = g a . tangent <$> ga {-# INLINE jacobianWithT #-}+#ifdef HLINT {-# ANN jacobianWithT "HLint: ignore Eta reduce" #-}+#endif -- | Compute the Jacobian using 'Forward' mode 'AD'. This must transpose the result, so 'jacobianT' is faster and allows more result types. -- -- -- >>> jacobian (\[x,y] -> [y,x,x+y,x*y,exp x * sin y]) [pi,1] -- [[0.0,1.0],[1.0,0.0],[1.0,1.0],[1.0,3.141592653589793],[19.472221418841606,12.502969588876512]]-jacobian :: (Traversable f, Traversable g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f a)-jacobian f as = transposeWith (const id) t p- where- (p, t) = bind' (fmap tangent . f) as+jacobian :: (Traversable f, Traversable g, Num a) => (forall s . f (Forward a s) -> g (Forward a s)) -> f a -> g (f a)+jacobian f as = transposeWith (const id) t p where+ (p, t) = bind' (fmap tangent . f) as {-# INLINE jacobian #-} -- | Compute the Jacobian using 'Forward' mode 'AD' and combine the output with the input. This must transpose the result, so 'jacobianWithT' is faster, and allows more result types.-jacobianWith :: (Traversable f, Traversable 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 = transposeWith (const id) t p- where- (p, t) = bindWith' g' f as- g' a ga = g a . tangent <$> ga+jacobianWith :: (Traversable f, Traversable g, Num a) => (a -> a -> b) -> (forall s. f (Forward a s) -> g (Forward a s)) -> f a -> g (f b)+jacobianWith g f as = transposeWith (const id) t p where+ (p, t) = bindWith' g' f as+ g' a ga = g a . tangent <$> ga {-# INLINE jacobianWith #-} -- | Compute the Jacobian using 'Forward' mode 'AD' along with the actual answer.-jacobian' :: (Traversable f, Traversable g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f a)-jacobian' f as = transposeWith row t p- where- (p, t) = bind' f as- row x as' = (primal x, tangent <$> as')+jacobian' :: (Traversable f, Traversable g, Num a) => (forall s. f (Forward a s) -> g (Forward a s)) -> f a -> g (a, f a)+jacobian' f as = transposeWith row t p where+ (p, t) = bind' f as+ row x as' = (primal x, tangent <$> as') {-# INLINE jacobian' #-} -- | Compute the Jacobian using 'Forward' mode 'AD' combined with the input using a user specified function, along with the actual answer.-jacobianWith' :: (Traversable f, Traversable 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 = transposeWith row t p- where- (p, t) = bindWith' g' f as- row x as' = (primal x, as')- g' a ga = g a . tangent <$> ga+jacobianWith' :: (Traversable f, Traversable g, Num a) => (a -> a -> b) -> (forall s. f (Forward a s) -> g (Forward a s)) -> f a -> g (a, f b)+jacobianWith' g f as = transposeWith row t p where+ (p, t) = bindWith' g' f as+ row x as' = (primal x, as')+ g' a ga = g a . tangent <$> ga {-# INLINE jacobianWith' #-} -- | Compute the gradient of a function using forward mode AD. -- -- Note, this performs /O(n)/ worse than 'Numeric.AD.Mode.Wengert.grad' for @n@ inputs, in exchange for better space utilization.-grad :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a+grad :: (Traversable f, Num a) => (forall s. f (Forward a s) -> Forward a s) -> f a -> f a grad f = bind (tangent . f) {-# INLINE grad #-} -- | Compute the gradient and answer to a function using forward mode AD. -- -- Note, this performs /O(n)/ worse than 'Numeric.AD.Mode.Wengert.grad'' for @n@ inputs, in exchange for better space utilization.-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 b, tangent <$> bs)- where- (b, bs) = bind' f as+grad' :: (Traversable f, Num a) => (forall s. f (Forward a s) -> Forward a s) -> f a -> (a, f a)+grad' f as = (primal b, tangent <$> bs) where+ (b, bs) = bind' f as {-# INLINE grad' #-} -- | Compute the gradient of a function using forward mode AD and combine the result with the input using a user-specified function. -- -- Note, this performs /O(n)/ worse than 'Numeric.AD.Mode.Wengert.gradWith' for @n@ inputs, in exchange for better space utilization.-gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f b+gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. f (Forward a s) -> Forward a s) -> f a -> f b gradWith g f = bindWith g (tangent . f) {-# INLINE gradWith #-} @@ -188,20 +183,17 @@ -- -- >>> gradWith' (,) sum [0..4] -- (10,[(0,1),(1,1),(2,1),(3,1),(4,1)])-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 $ f (AD . Lift <$> as), bindWith g (tangent . f) as)+gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. f (Forward a s) -> Forward a s) -> f a -> (a, f b)+gradWith' g f as = (primal $ f (Lift <$> as), bindWith g (tangent . f) as) {-# INLINE gradWith' #-} -- | Compute the product of a vector with the Hessian using forward-on-forward-mode AD. ---hessianProduct :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> f a-hessianProduct f = duF $ grad $ decomposeMode . f . fmap composeMode+hessianProduct :: (Traversable f, Num a) => (forall s s'. f (On (Forward (Forward a s') s)) -> On (Forward (Forward a s') s)) -> f (a, a) -> f a+hessianProduct f = duF $ grad $ off . f . fmap On+{-# INLINE hessianProduct #-} -- | Compute the gradient and hessian product using forward-on-forward-mode AD.-hessianProduct' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> f (a, a)-hessianProduct' f = duF' $ grad $ decomposeMode . f . fmap composeMode---- * Experimental---- data f :> a = a :< f (f :> a)--- gradients :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (f :> a)+hessianProduct' :: (Traversable f, Num a) => (forall s s'. f (On (Forward (Forward a s') s)) -> On (Forward (Forward a s') s)) -> f (a, a) -> f (a, a)+hessianProduct' f = duF' $ grad $ off . f . fmap On+{-# INLINE hessianProduct' #-}
+ src/Numeric/AD/Mode/Forward/Double.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE RankNTypes #-}+module Numeric.AD.Mode.Forward.Double+ ( ForwardDouble+ -- * Gradient+ , grad+ , grad'+ , gradWith+ , gradWith'+ -- * Jacobian+ , jacobian+ , jacobian'+ , jacobianWith+ , jacobianWith'+ -- * Transposed Jacobian+ , jacobianT+ , jacobianWithT+ -- * Derivatives+ , diff+ , diff'+ , diffF+ , diffF'+ -- * Directional Derivatives+ , du+ , du'+ , duF+ , duF'+ ) where++import Control.Applicative+import Data.Traversable (Traversable)+import Numeric.AD.Mode+import Numeric.AD.Internal.Forward.Double++-- | Compute the directional derivative of a function given a zipped up 'Functor' of the input values and their derivatives+du :: Functor f => (forall s. f (ForwardDouble s) -> ForwardDouble s) -> f (Double, Double) -> Double+du f = tangent . f . fmap (uncurry bundle)+{-# INLINE du #-}++-- | Compute the answer and directional derivative of a function given a zipped up 'Functor' of the input values and their derivatives+du' :: Functor f => (forall s. f (ForwardDouble s) -> ForwardDouble s) -> f (Double, Double) -> (Double, Double)+du' f = unbundle . f . fmap (uncurry bundle)+{-# INLINE du' #-}++-- | Compute a vector of directional derivatives for a function given a zipped up 'Functor' of the input values and their derivatives.+duF :: (Functor f, Functor g) => (forall s. f (ForwardDouble s) -> g (ForwardDouble s)) -> f (Double, Double) -> g Double+duF f = fmap tangent . f . fmap (uncurry bundle)+{-# INLINE duF #-}++-- | Compute a vector of answers and directional derivatives for a function given a zipped up 'Functor' of the input values and their derivatives.+duF' :: (Functor f, Functor g) => (forall s. f (ForwardDouble s) -> g (ForwardDouble s)) -> f (Double, Double) -> g (Double, Double)+duF' f = fmap unbundle . f . fmap (uncurry bundle)+{-# INLINE duF' #-}++-- | The 'diff' function calculates the first derivative of a scalar-to-scalar function by forward-mode 'AD'+--+-- >>> diff sin 0+-- 1.0+diff :: (forall s. ForwardDouble s -> ForwardDouble s) -> Double -> Double+diff f a = tangent $ apply f a+{-# INLINE diff #-}++-- | The 'diff'' function calculates the result and first derivative of scalar-to-scalar function by 'Forward' mode 'AD'+--+-- @+-- 'diff'' 'sin' == 'sin' 'Control.Arrow.&&&' 'cos'+-- 'diff'' f = f 'Control.Arrow.&&&' d f+-- @+--+-- >>> diff' sin 0+-- (0.0,1.0)+--+-- >>> diff' exp 0+-- (1.0,1.0)+diff' :: (forall s. ForwardDouble s -> ForwardDouble s) -> Double -> (Double, Double)+diff' f a = unbundle $ apply f a+{-# INLINE diff' #-}++-- | The 'diffF' function calculates the first derivatives of scalar-to-nonscalar function by 'Forward' mode 'AD'+--+-- >>> diffF (\a -> [sin a, cos a]) 0+-- [1.0,-0.0]+diffF :: Functor f => (forall s. ForwardDouble s -> f (ForwardDouble s)) -> Double -> f Double+diffF f a = tangent <$> apply f a+{-# INLINE diffF #-}++-- | The 'diffF'' function calculates the result and first derivatives of a scalar-to-non-scalar function by 'Forward' mode 'AD'+--+-- >>> diffF' (\a -> [sin a, cos a]) 0+-- [(0.0,1.0),(1.0,-0.0)]+diffF' :: Functor f => (forall s. ForwardDouble s -> f (ForwardDouble s)) -> Double -> f (Double, Double)+diffF' f a = unbundle <$> apply f a+{-# INLINE diffF' #-}++-- | A fast, simple, transposed Jacobian computed with forward-mode AD.+jacobianT :: (Traversable f, Functor g) => (forall s. f (ForwardDouble s) -> g (ForwardDouble s)) -> f Double -> f (g Double)+jacobianT f = bind (fmap tangent . f)+{-# INLINE jacobianT #-}++-- | A fast, simple, transposed Jacobian computed with 'Forward' mode 'AD' that combines the output with the input.+jacobianWithT :: (Traversable f, Functor g) => (Double -> Double -> b) -> (forall s. f (ForwardDouble s) -> g (ForwardDouble s)) -> f Double -> f (g b)+jacobianWithT g f = bindWith g' f where+ g' a ga = g a . tangent <$> ga+{-# INLINE jacobianWithT #-}+{-# ANN jacobianWithT "HLint: ignore Eta reduce" #-}++-- | Compute the Jacobian using 'Forward' mode 'AD'. This must transpose the result, so 'jacobianT' is faster and allows more result types.+--+--+-- >>> jacobian (\[x,y] -> [y,x,x+y,x*y,exp x * sin y]) [pi,1]+-- [[0.0,1.0],[1.0,0.0],[1.0,1.0],[1.0,3.141592653589793],[19.472221418841606,12.502969588876512]]+jacobian :: (Traversable f, Traversable g) => (forall s . f (ForwardDouble s) -> g (ForwardDouble s)) -> f Double -> g (f Double)+jacobian f as = transposeWith (const id) t p where+ (p, t) = bind' (fmap tangent . f) as+{-# INLINE jacobian #-}++-- | Compute the Jacobian using 'Forward' mode 'AD' and combine the output with the input. This must transpose the result, so 'jacobianWithT' is faster, and allows more result types.+jacobianWith :: (Traversable f, Traversable g) => (Double -> Double -> b) -> (forall s. f (ForwardDouble s) -> g (ForwardDouble s)) -> f Double -> g (f b)+jacobianWith g f as = transposeWith (const id) t p where+ (p, t) = bindWith' g' f as+ g' a ga = g a . tangent <$> ga+{-# INLINE jacobianWith #-}++-- | Compute the Jacobian using 'Forward' mode 'AD' along with the actual answer.+jacobian' :: (Traversable f, Traversable g) => (forall s. f (ForwardDouble s) -> g (ForwardDouble s)) -> f Double -> g (Double, f Double)+jacobian' f as = transposeWith row t p where+ (p, t) = bind' f as+ row x as' = (primal x, tangent <$> as')+{-# INLINE jacobian' #-}++-- | Compute the Jacobian using 'Forward' mode 'AD' combined with the input using a user specified function, along with the actual answer.+jacobianWith' :: (Traversable f, Traversable g) => (Double -> Double -> b) -> (forall s. f (ForwardDouble s) -> g (ForwardDouble s)) -> f Double -> g (Double, f b)+jacobianWith' g f as = transposeWith row t p where+ (p, t) = bindWith' g' f as+ row x as' = (primal x, as')+ g' a ga = g a . tangent <$> ga+{-# INLINE jacobianWith' #-}++-- | Compute the gradient of a function using forward mode AD.+--+-- Note, this performs /O(n)/ worse than 'Numeric.AD.Mode.Wengert.grad' for @n@ inputs, in exchange for better space utilization.+grad :: (Traversable f) => (forall s. f (ForwardDouble s) -> ForwardDouble s) -> f Double -> f Double+grad f = bind (tangent . f)+{-# INLINE grad #-}++-- | Compute the gradient and answer to a function using forward mode AD.+--+-- Note, this performs /O(n)/ worse than 'Numeric.AD.Mode.Wengert.grad'' for @n@ inputs, in exchange for better space utilization.+grad' :: (Traversable f) => (forall s. f (ForwardDouble s) -> ForwardDouble s) -> f Double -> (Double, f Double)+grad' f as = (primal b, tangent <$> bs)+ where+ (b, bs) = bind' f as+{-# INLINE grad' #-}++-- | Compute the gradient of a function using forward mode AD and combine the result with the input using a user-specified function.+--+-- Note, this performs /O(n)/ worse than 'Numeric.AD.Mode.Wengert.gradWith' for @n@ inputs, in exchange for better space utilization.+gradWith :: (Traversable f) => (Double -> Double -> b) -> (forall s. f (ForwardDouble s) -> ForwardDouble s) -> f Double -> f b+gradWith g f = bindWith g (tangent . f)+{-# INLINE gradWith #-}++-- | Compute the gradient of a function using forward mode AD and the answer, and combine the result with the input using a+-- user-specified function.+--+-- Note, this performs /O(n)/ worse than 'Numeric.AD.Mode.Wengert.gradWith'' for @n@ inputs, in exchange for better space utilization.+--+-- >>> gradWith' (,) sum [0..4]+-- (10.0,[(0.0,1.0),(1.0,1.0),(2.0,1.0),(3.0,1.0),(4.0,1.0)])+gradWith' :: (Traversable f) => (Double -> Double -> b) -> (forall s. f (ForwardDouble s) -> ForwardDouble s) -> f Double -> (Double, f b)+gradWith' g f as = (primal $ f (auto <$> as), bindWith g (tangent . f) as)+{-# INLINE gradWith' #-}
src/Numeric/AD/Mode/Kahn.hs view
@@ -1,8 +1,13 @@-{-# LANGUAGE Rank2Types, TemplateHaskell, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE UndecidableInstances #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Mode.Kahn--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -19,61 +24,57 @@ ----------------------------------------------------------------------------- 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+ ( Kahn+ -- * Gradient+ , grad+ , grad'+ , gradWith+ , gradWith'+ -- * Jacobian+ , jacobian+ , jacobian'+ , jacobianWith+ , jacobianWith'+ -- * Hessian+ , hessian+ , hessianF+ -- * Derivatives+ , diff+ , diff'+ , diffF+ , diffF'+ -- * Unsafe Variadic Gradient+ -- $vgrad+ , vgrad, vgrad'+ , Grad+ ) where import Control.Applicative ((<$>))+import Data.Functor.Compose import Data.Traversable (Traversable)--import Numeric.AD.Types-import Numeric.AD.Internal.Classes-import Numeric.AD.Internal.Composition+import Numeric.AD.Internal.On 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.+-- | The 'grad' function calculates the gradient of a non-scalar-to-scalar function with kahn-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 :: (Traversable f, Num a) => (forall s. f (Kahn a s) -> Kahn a s) -> 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.+-- | The 'grad'' function calculates the result and gradient of a non-scalar-to-scalar function with kahn-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+grad' :: (Traversable f, Num a) => (forall s. f (Kahn a s) -> Kahn a s) -> 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.+-- | @'grad' g f@ function calculates the gradient of a non-scalar-to-scalar function @f@ with kahn-mode AD in a single pass. -- The gradient is combined element-wise with the argument using the function @g@. -- -- @@@ -82,46 +83,46 @@ -- @ -- ---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 :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. f (Kahn a s) -> Kahn a s) -> 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+-- | @'grad'' g f@ calculates the result and gradient of a non-scalar-to-scalar function @f@ with kahn-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+gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. f (Kahn a s) -> Kahn a s) -> 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.+-- | The 'jacobian' function calculates the jacobian of a non-scalar-to-non-scalar function with kahn 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 :: (Traversable f, Functor g, Num a) => (forall s. f (Kahn a s) -> g (Kahn a s)) -> f a -> g (f a) jacobian f as = unbind vs . partialArray bds <$> f vs where- (vs, bds) = bind as+ (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,+-- | The 'jacobian'' function calculates both the result and the Jacobian of a nonscalar-to-nonscalar function, using @m@ invocations of kahn 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' :: (Traversable f, Functor g, Num a) => (forall s. f (Kahn a s) -> g (Kahn a s)) -> 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))+ (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.+-- | 'jacobianWith g f' calculates the Jacobian of a non-scalar-to-non-scalar function @f@ with kahn 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@. --@@ -129,21 +130,21 @@ -- '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 :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. f (Kahn a s) -> g (Kahn a s)) -> f a -> g (f b) jacobianWith g f as = unbindWith g vs . partialArray bds <$> f vs where- (vs, bds) = bind as+ (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,+-- | 'jacobianWith' g f' calculates both the result and the Jacobian of a nonscalar-to-nonscalar function @f@, using @m@ invocations of kahn 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' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. f (Kahn a s) -> g (Kahn a s)) -> 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))+ (vs, bds) = bind as+ row a = (primal a, unbindWith g vs (partialArray bds a)) {-# INLINE jacobianWith' #-} -- | Compute the derivative of a function.@@ -153,7 +154,7 @@ -- -- >>> cos 0 -- 1.0-diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a+diff :: Num a => (forall s. Kahn a s -> Kahn a s) -> a -> a diff f a = derivative $ f (var a 0) {-# INLINE diff #-} @@ -163,7 +164,7 @@ -- -- >>> diff' sin 0 -- (0.0,1.0)-diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)+diff' :: Num a => (forall s. Kahn a s -> Kahn a s) -> a -> (a, a) diff' f a = derivative' $ f (var a 0) {-# INLINE diff' #-} @@ -171,7 +172,7 @@ -- -- >>> 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 :: (Functor f, Num a) => (forall s. Kahn a s -> f (Kahn a s)) -> a -> f a diffF f a = derivative <$> f (var a 0) {-# INLINE diffF #-} @@ -180,25 +181,35 @@ -- -- >>> 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' :: (Functor f, Num a) => (forall s. Kahn a s -> f (Kahn a s)) -> 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.++-- | Compute the 'hessian' via the 'jacobian' of the gradient. gradient is computed in 'Kahn' mode and then the 'jacobian' is computed in 'Kahn' 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))+hessian :: (Traversable f, Num a) => (forall s s'. f (On (Kahn (Kahn a s') s)) -> (On (Kahn (Kahn a s') s))) -> f a -> f (f a)+hessian f = jacobian (grad (off . f . fmap On)) --- | 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.+-- | Compute the order 3 Hessian tensor on a non-scalar-to-non-scalar function via the 'Kahn'-mode Jacobian of the 'Kahn'-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))+hessianF :: (Traversable f, Functor g, Num a) => (forall s s'. f (On (Kahn (Kahn a s') s)) -> g (On (Kahn (Kahn a s') s))) -> f a -> g (f (f a))+hessianF f = getCompose . jacobian (Compose . jacobian (fmap off . f . fmap On)) ++-- $vgrad+--+-- Variadic combinators for variadic mixed-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 'auto' calls you use when taking the gradient+-- of a function that takes gradients!
src/Numeric/AD/Mode/Reverse.hs view
@@ -1,8 +1,14 @@-{-# LANGUAGE Rank2Types, TemplateHaskell, BangPatterns, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Mode.Reverse--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -14,56 +20,54 @@ ----------------------------------------------------------------------------- module Numeric.AD.Mode.Reverse- (- -- * Gradient- grad- , grad'- , gradWith- , gradWith'+ ( Reverse+ -- * Gradient+ , grad+ , grad'+ , gradWith+ , gradWith' - -- * Jacobian- , jacobian- , jacobian'- , jacobianWith- , jacobianWith'+ -- * Jacobian+ , jacobian+ , jacobian'+ , jacobianWith+ , jacobianWith' - -- * Hessian- , hessian- , hessianF+ -- * Hessian+ , hessian+ , hessianF - -- * Derivatives- , diff- , diff'- , diffF- , diffF'- ) where+ -- * Derivatives+ , diff+ , diff'+ , diffF+ , diffF'+ ) where import Control.Applicative ((<$>))+import Data.Functor.Compose+import Data.Reflection (Reifies) import Data.Traversable (Traversable)--import Numeric.AD.Types-import Numeric.AD.Internal.Classes-import Numeric.AD.Internal.Composition+import Numeric.AD.Internal.On 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-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+grad :: (Traversable f, Num a) => (forall s. Reifies s Tape => f (Reverse a s) -> Reverse a s) -> f a -> f a+grad f as = reifyTape (snd bds) $ \p -> unbind vs $! partialArrayOf p 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] -> 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)+grad' :: (Traversable f, Num a) => (forall s. Reifies s Tape => f (Reverse a s) -> Reverse a s) -> f a -> (a, f a)+grad' f as = reifyTape (snd bds) $ \p -> case f vs of+ r -> (primal r, unbind vs $! partialArrayOf p bds $! r) where (vs, bds) = bind as {-# INLINE grad' #-} @@ -74,8 +78,8 @@ -- '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+gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Reifies s Tape => f (Reverse a s) -> Reverse a s) -> f a -> f b+gradWith g f as = reifyTape (snd bds) $ \p -> unbindWith g vs $! partialArrayOf p bds $! f vs where (vs,bds) = bind as {-# INLINE gradWith #-} @@ -85,19 +89,19 @@ -- @ -- '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+gradWith' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Reifies s Tape => f (Reverse a s) -> Reverse a s) -> f a -> (a, f b)+gradWith' g f as = reifyTape (snd bds) $ \p -> case f vs of+ r -> (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+jacobian :: (Traversable f, Functor g, Num a) => (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> f a -> g (f a)+jacobian f as = reifyTape (snd bds) $ \p -> unbind vs . partialArrayOf p 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,@@ -106,10 +110,10 @@ -- -- >>> 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' :: (Traversable f, Functor g, Num a) => (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> 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)+ in row <$> f vs where (vs, bds) = bind as {-# INLINE jacobian' #-} @@ -121,9 +125,9 @@ -- '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+jacobianWith :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> f a -> g (f b)+jacobianWith g f as = reifyTape (snd bds) $ \p -> unbindWith g vs . partialArrayOf p 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,@@ -133,10 +137,10 @@ -- -- @'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' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. Reifies s Tape => f (Reverse a s) -> g (Reverse a s)) -> 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)+ in row <$> f vs where (vs, bds) = bind as {-# INLINE jacobianWith' #-} @@ -144,7 +148,7 @@ -- -- >>> diff sin 0 -- 1.0-diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a+diff :: Num a => (forall s. Reifies s Tape => Reverse a s -> Reverse a s) -> a -> a diff f a = reifyTape 1 $ \p -> derivativeOf p $! f (var a 0) {-# INLINE diff #-} @@ -155,7 +159,7 @@ -- -- >>> diff' exp 0 -- (1.0,1.0)-diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)+diff' :: Num a => (forall s. Reifies s Tape => Reverse a s -> Reverse a s) -> a -> (a, a) diff' f a = reifyTape 1 $ \p -> derivativeOf' p $! f (var a 0) {-# INLINE diff' #-} @@ -164,7 +168,7 @@ -- >>> 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 :: (Functor f, Num a) => (forall s. Reifies s Tape => Reverse a s -> f (Reverse a s)) -> a -> f a diffF f a = reifyTape 1 $ \p -> derivativeOf p <$> f (var a 0) {-# INLINE diffF #-} @@ -172,7 +176,7 @@ -- -- >>> 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' :: (Functor f, Num a) => (forall s. Reifies s Tape => Reverse a s -> f (Reverse a s)) -> a -> f (a, a) diffF' f a = reifyTape 1 $ \p -> derivativeOf' p <$> f (var a 0) {-# INLINE diffF' #-} @@ -182,8 +186,9 @@ -- -- >>> 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))+hessian :: (Traversable f, Num a) => (forall s s'. (Reifies s Tape, Reifies s' Tape) => f (On (Reverse (Reverse a s') s)) -> (On (Reverse (Reverse a s') s))) -> f a -> f (f a)+hessian f = jacobian (grad (off . f . fmap On))+{-# INLINE hessian #-} -- | 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. --@@ -191,5 +196,6 @@ -- -- >>> 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))+hessianF :: (Traversable f, Functor g, Num a) => (forall s s'. (Reifies s Tape, Reifies s' Tape) => f (On (Reverse (Reverse a s') s)) -> g (On (Reverse (Reverse a s') s))) -> f a -> g (f (f a))+hessianF f = getCompose . jacobian (Compose . jacobian (fmap off . f . fmap On))+{-# INLINE hessianF #-}
src/Numeric/AD/Mode/Sparse.hs view
@@ -1,8 +1,7 @@ {-# LANGUAGE Rank2Types #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Mode.Sparse--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -13,41 +12,42 @@ ----------------------------------------------------------------------------- module Numeric.AD.Mode.Sparse- (- -- * Sparse Gradients- grad- , grad'- , gradWith- , gradWith'- , grads-- -- * Sparse Jacobians (synonyms)- , jacobian- , jacobian'- , jacobianWith- , jacobianWith'- , jacobians+ ( Sparse+ -- * Sparse Gradients+ , grad+ , grad'+ , gradWith+ , gradWith'+ -- * Variadic Gradients+ -- $vgrad+ , Grad+ , vgrad+ -- * Higher-Order Gradients+ , grads+ -- * Variadic Higher-Order Gradients+ , Grads+ , vgrads - -- * Sparse Hessians- , hessian- , hessian'+ -- * Sparse Jacobians (synonyms)+ , jacobian+ , jacobian'+ , jacobianWith+ , jacobianWith'+ , jacobians - , hessianF- , hessianF'+ -- * Sparse Hessians+ , hessian+ , hessian' - -- * Unsafe gradients- , vgrad- , vgrads+ , hessianF+ , hessianF' - -- * Exposed Types- , Grad- , Grads- ) where+ ) where import Control.Comonad import Data.Traversable import Control.Comonad.Cofree-import Numeric.AD.Types+import Numeric.AD.Jet import Numeric.AD.Internal.Sparse import Numeric.AD.Internal.Combinators @@ -55,43 +55,43 @@ second g (a,b) = (a, g b) {-# INLINE second #-} -grad :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f a+grad :: (Traversable f, Num a) => (forall s. f (Sparse a s) -> Sparse a s) -> f a -> f a grad f as = d as $ apply f as {-# INLINE grad #-} -grad' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f a)+grad' :: (Traversable f, Num a) => (forall s. f (Sparse a s) -> Sparse a s) -> f a -> (a, f a) grad' f as = d' as $ apply f as {-# INLINE grad' #-} -gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f b+gradWith :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. f (Sparse a s) -> Sparse a s) -> f a -> f b gradWith g f as = zipWithT g as $ grad f as {-# INLINE gradWith #-} -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' :: (Traversable f, Num a) => (a -> a -> b) -> (forall s. f (Sparse a s) -> Sparse a s) -> f a -> (a, f b) gradWith' g f as = second (zipWithT g as) $ grad' f as {-# INLINE gradWith' #-} -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 :: (Traversable f, Functor g, Num a) => (forall s. f (Sparse a s) -> g (Sparse a s)) -> f a -> g (f a) jacobian f as = d as <$> apply f as {-# INLINE jacobian #-} -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' :: (Traversable f, Functor g, Num a) => (forall s. f (Sparse a s) -> g (Sparse a s)) -> f a -> g (a, f a) jacobian' f as = d' as <$> apply f as {-# INLINE jacobian' #-} -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 :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. f (Sparse a s) -> g (Sparse a s)) -> f a -> g (f b) jacobianWith g f as = zipWithT g as <$> jacobian f as {-# INLINE jacobianWith #-} -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' :: (Traversable f, Functor g, Num a) => (a -> a -> b) -> (forall s. f (Sparse a s) -> g (Sparse a s)) -> f a -> g (a, f b) jacobianWith' g f as = second (zipWithT g as) <$> jacobian' f as {-# INLINE jacobianWith' #-} -grads :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> Cofree f a+grads :: (Traversable f, Num a) => (forall s. f (Sparse a s) -> Sparse a s) -> f a -> Cofree f a grads f as = ds as $ apply f as {-# INLINE grads #-} -jacobians :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (Cofree f a)+jacobians :: (Traversable f, Functor g, Num a) => (forall s. f (Sparse a s) -> g (Sparse a s)) -> f a -> g (Cofree f a) jacobians f as = ds as <$> apply f as {-# INLINE jacobians #-} @@ -103,18 +103,27 @@ d2' (a :< as) = (a, fmap (\(da :< das) -> (da, extract <$> das)) as) {-# INLINE d2' #-} -hessian :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f (f a)+hessian :: (Traversable f, Num a) => (forall s. f (Sparse a s) -> Sparse a s) -> f a -> f (f a) hessian f as = d2 $ grads f as {-# INLINE hessian #-} -hessian' :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> (a, f (a, f a))+hessian' :: (Traversable f, Num a) => (forall s. f (Sparse a s) -> Sparse a s) -> f a -> (a, f (a, f a)) hessian' f as = d2' $ grads f as {-# INLINE hessian' #-} -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 :: (Traversable f, Functor g, Num a) => (forall s. f (Sparse a s) -> g (Sparse a s)) -> f a -> g (f (f a)) hessianF f as = d2 <$> jacobians f as {-# INLINE hessianF #-} -hessianF' :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (a, f (a, f a))+hessianF' :: (Traversable f, Functor g, Num a) => (forall s. f (Sparse a s) -> g (Sparse a s)) -> f a -> g (a, f (a, f a)) hessianF' f as = d2' <$> jacobians f as {-# INLINE hessianF' #-}++-- $vgrad+--+-- Variadic combinators for variadic mixed-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 'auto' calls you use when taking the gradient+-- of a function that takes gradients!
src/Numeric/AD/Mode/Tower.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE Rank2Types, BangPatterns #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE BangPatterns #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Mode.Tower--- Copyright : (c) Edward Kmett 2010+-- Copyright : (c) Edward Kmett 2010-2014 -- License : BSD3 -- Maintainer : ekmett@gmail.com -- Stability : experimental@@ -13,111 +13,103 @@ ----------------------------------------------------------------------------- module Numeric.AD.Mode.Tower- (- -- * Taylor Series- taylor- , taylor0- -- * Maclaurin Series- , maclaurin- , maclaurin0- -- * Derivatives- , diff -- first derivative of (a -> a)- , diff' -- answer and first derivative of (a -> a)- , diffs -- answer and all derivatives of (a -> a)- , diffs0 -- zero padded derivatives of (a -> a)- , diffsF -- answer and all derivatives of (a -> f a)- , diffs0F -- zero padded derivatives of (a -> f a)- -- * Directional Derivatives- , du -- directional derivative of (a -> a)- , du' -- answer and directional derivative of (a -> a)- , dus -- answer and all directional derivatives of (a -> a)- , dus0 -- answer and all zero padded directional derivatives of (a -> a)- , duF -- directional derivative of (a -> f a)- , duF' -- answer and directional derivative of (a -> f a)- , dusF -- answer and all directional derivatives of (a -> f a)- , dus0F -- answer and all zero padded directional derivatives of (a -> a)- ) where+ ( Tower+ -- * Taylor Series+ , taylor+ , taylor0+ -- * Maclaurin Series+ , maclaurin+ , maclaurin0+ -- * Derivatives+ , diff -- first derivative of (a -> a)+ , diff' -- answer and first derivative of (a -> a)+ , diffs -- answer and all derivatives of (a -> a)+ , diffs0 -- zero padded derivatives of (a -> a)+ , diffsF -- answer and all derivatives of (a -> f a)+ , diffs0F -- zero padded derivatives of (a -> f a)+ -- * Directional Derivatives+ , du -- directional derivative of (a -> a)+ , du' -- answer and directional derivative of (a -> a)+ , dus -- answer and all directional derivatives of (a -> a)+ , dus0 -- answer and all zero padded directional derivatives of (a -> a)+ , duF -- directional derivative of (a -> f a)+ , duF' -- answer and directional derivative of (a -> f a)+ , dusF -- answer and all directional derivatives of (a -> f a)+ , dus0F -- answer and all zero padded directional derivatives of (a -> a)+ ) where import Control.Applicative ((<$>))-import Numeric.AD.Types import Numeric.AD.Internal.Tower -diffs :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+diffs :: Num a => (forall s. Tower a s -> Tower a s) -> a -> [a] diffs f a = getADTower $ apply f a {-# INLINE diffs #-} -diffs0 :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+diffs0 :: Num a => (forall s. Tower a s -> Tower a s) -> a -> [a] diffs0 f a = zeroPad (diffs f a) {-# INLINE diffs0 #-} -diffsF :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f [a]+diffsF :: (Functor f, Num a) => (forall s. Tower a s -> f (Tower a s)) -> a -> f [a] diffsF f a = getADTower <$> apply f a {-# INLINE diffsF #-} -diffs0F :: (Functor f, Num a) => (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f [a]+diffs0F :: (Functor f, Num a) => (forall s. Tower a s -> f (Tower a s)) -> a -> f [a] diffs0F f a = (zeroPad . getADTower) <$> apply f a {-# INLINE diffs0F #-} -taylor :: Fractional a => (forall s. Mode s => AD s a -> AD s a) -> a -> a -> [a]-taylor f x dx = go 1 1 (diffs f x)- where- go !n !acc (a:as) = a * acc : go (n + 1) (acc * dx / n) as- go _ _ [] = []+taylor :: Fractional a => (forall s. Tower a s -> Tower a s) -> a -> a -> [a]+taylor f x dx = go 1 1 (diffs f x) where+ go !n !acc (a:as) = a * acc : go (n + 1) (acc * dx / n) as+ go _ _ [] = [] -taylor0 :: Fractional a => (forall s. Mode s => AD s a -> AD s a) -> a -> a -> [a]+taylor0 :: Fractional a => (forall s. Tower a s -> Tower a s) -> a -> a -> [a] taylor0 f x dx = zeroPad (taylor f x dx) {-# INLINE taylor0 #-} -maclaurin :: Fractional a => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+maclaurin :: Fractional a => (forall s. Tower a s -> Tower a s) -> a -> [a] maclaurin f = taylor f 0 {-# INLINE maclaurin #-} -maclaurin0 :: Fractional a => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+maclaurin0 :: Fractional a => (forall s. Tower a s -> Tower a s) -> a -> [a] maclaurin0 f = taylor0 f 0 {-# INLINE maclaurin0 #-} -diff :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> a+diff :: Num a => (forall s. Tower a s -> Tower a s) -> a -> a diff f = d . diffs f {-# INLINE diff #-} -diff' :: Num a => (forall s. Mode s => AD s a -> AD s a) -> a -> (a, a)+diff' :: Num a => (forall s. Tower a s -> Tower a s) -> a -> (a, a) diff' f = d' . diffs f {-# INLINE diff' #-} -du :: (Functor f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> a+du :: (Functor f, Num a) => (forall s. f (Tower a s) -> Tower a s) -> f (a, a) -> a du f = d . getADTower . f . fmap withD {-# INLINE du #-} -du' :: (Functor f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f (a, a) -> (a, a)+du' :: (Functor f, Num a) => (forall s. f (Tower a s) -> Tower a s) -> f (a, a) -> (a, a) du' f = d' . getADTower . f . fmap withD {-# INLINE du' #-} -duF :: (Functor f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f (a, a) -> g a+duF :: (Functor f, Functor g, Num a) => (forall s. f (Tower a s) -> g (Tower a s)) -> f (a, a) -> g a duF f = fmap (d . getADTower) . f . fmap withD {-# INLINE duF #-} -duF' :: (Functor f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f (a, a) -> g (a, a)+duF' :: (Functor f, Functor g, Num a) => (forall s. f (Tower a s) -> g (Tower a s)) -> f (a, a) -> g (a, a) duF' f = fmap (d' . getADTower) . f . fmap withD {-# INLINE duF' #-} -dus :: (Functor f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f [a] -> [a]+dus :: (Functor f, Num a) => (forall s. f (Tower a s) -> Tower a s) -> f [a] -> [a] dus f = getADTower . f . fmap tower {-# INLINE dus #-} -dus0 :: (Functor f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f [a] -> [a]+dus0 :: (Functor f, Num a) => (forall s. f (Tower a s) -> Tower a s) -> f [a] -> [a] dus0 f = zeroPad . getADTower . f . fmap tower {-# INLINE dus0 #-} -dusF :: (Functor f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f [a] -> g [a]+dusF :: (Functor f, Functor g, Num a) => (forall s. f (Tower a s) -> g (Tower a s)) -> f [a] -> g [a] dusF f = fmap getADTower . f . fmap tower {-# INLINE dusF #-} -dus0F :: (Functor f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f [a] -> g [a]+dus0F :: (Functor f, Functor g, Num a) => (forall s. f (Tower a s) -> g (Tower a s)) -> f [a] -> g [a] dus0F f = fmap getADTower . f . fmap tower {-# INLINE dus0F #-}---- TODO: higher order gradients--- data f :> a = a :< f (f :> a)--- gradients :: (Traversable f, Num a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> f :> a--- gradientsF, jacobians :: (Traversable f, Functor g, Num a) => (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g (f :> a)--- gradientsM :: (Traversable f, Monad m, Num a) => FF f m a -> f a -> m (f :> a)
src/Numeric/AD/Newton.hs view
@@ -1,7 +1,10 @@-{-# LANGUAGE Rank2Types, BangPatterns, ScopedTypeVariables #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-} ----------------------------------------------------------------------------- -- |--- Module : Numeric.AD.Newton -- Copyright : (c) Edward Kmett 2010 -- License : BSD3 -- Maintainer : ekmett@gmail.com@@ -11,29 +14,34 @@ ----------------------------------------------------------------------------- module Numeric.AD.Newton- (- -- * Newton's Method (Forward AD)- findZero- , inverse- , fixedPoint- , extremum- -- * Gradient Ascent/Descent (Reverse AD)- , gradientDescent- , gradientAscent- , conjugateGradientDescent- , conjugateGradientAscent- ) where+ (+ -- * Newton's Method (Forward AD)+ findZero+ , inverse+ , fixedPoint+ , extremum+ -- * Gradient Ascent/Descent (Reverse AD)+ , gradientDescent+ , gradientAscent+ , conjugateGradientDescent+ , conjugateGradientAscent+ ) where import Prelude hiding (all, mapM, sum)-import Data.Functor import Data.Foldable (all, sum)+import Data.Reflection (Reifies) import Data.Traversable-import Numeric.AD.Types+import Numeric.AD.Mode import Numeric.AD.Mode.Forward (diff, diff') import Numeric.AD.Mode.Reverse (grad, gradWith') import Numeric.AD.Internal.Combinators-import Numeric.AD.Internal.Composition+import Numeric.AD.Internal.Forward (Forward)+import Numeric.AD.Internal.On+import Numeric.AD.Internal.Reverse (Reverse, Tape) +-- $setup+-- >>> import Data.Complex+ -- | The 'findZero' function finds a zero of a scalar function using -- Newton's method; its output is a stream of increasingly accurate -- results. (Modulo the usual caveats.) If the stream becomes constant@@ -44,10 +52,9 @@ -- >>> take 10 $ findZero (\x->x^2-4) 1 -- [1.0,2.5,2.05,2.000609756097561,2.0000000929222947,2.000000000000002,2.0] ----- >>> import Data.Complex -- >>> last $ take 10 $ findZero ((+1).(^2)) (1 :+ 1) -- 0.0 :+ 1.0-findZero :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+findZero :: (Fractional a, Eq a) => (forall s. Forward a s -> Forward a s) -> a -> [a] findZero f = go where go x = x : if x == xn then [] else go xn where (y,y') = diff' f x@@ -63,7 +70,7 @@ -- -- >>> last $ take 10 $ inverse sqrt 1 (sqrt 10) -- 10.0-inverse :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> a -> [a]+inverse :: (Fractional a, Eq a) => (forall s. Forward a s -> Forward a s) -> a -> a -> [a] inverse f x0 y = findZero (\x -> f x - auto y) x0 {-# INLINE inverse #-} @@ -76,7 +83,7 @@ -- -- >>> last $ take 10 $ fixedPoint cos 1 -- 0.7390851332151607-fixedPoint :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]+fixedPoint :: (Fractional a, Eq a) => (forall s. Forward a s -> Forward a s) -> a -> [a] fixedPoint f = findZero (\x -> f x - x) {-# INLINE fixedPoint #-} @@ -87,8 +94,8 @@ -- -- >>> last $ take 10 $ extremum cos 1 -- 0.0-extremum :: (Fractional a, Eq a) => (forall s. Mode s => AD s a -> AD s a) -> a -> [a]-extremum f = findZero (diff (decomposeMode . f . composeMode))+extremum :: (Fractional a, Eq a) => (forall s s'. On (Forward (Forward a s') s) -> On (Forward (Forward a s') s)) -> a -> [a]+extremum f = findZero (diff (off . f . On)) {-# INLINE extremum #-} -- | The 'gradientDescent' function performs a multivariate@@ -98,44 +105,53 @@ -- increasingly accurate results. (Modulo the usual caveats.) -- -- It uses reverse mode automatic differentiation to compute the gradient.-gradientDescent :: (Traversable f, Fractional a, Ord a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> [f a]+gradientDescent :: (Traversable f, Fractional a, Ord a) => (forall s. Reifies s Tape => f (Reverse a s) -> Reverse a s) -> f a -> [f a] gradientDescent f x0 = go x0 fx0 xgx0 0.1 (0 :: Int)- where- (fx0, xgx0) = gradWith' (,) f x0- go x fx xgx !eta !i- | eta == 0 = [] -- step size is 0- | fx1 > fx = go x fx xgx (eta/2) 0 -- we stepped too far- | zeroGrad xgx = [] -- gradient is 0- | otherwise = x1 : if i == 10- then go x1 fx1 xgx1 (eta*2) 0- else go x1 fx1 xgx1 eta (i+1)- where- zeroGrad = all (\(_,g) -> g == 0)- x1 = fmap (\(xi,gxi) -> xi - eta * gxi) xgx- (fx1, xgx1) = gradWith' (,) f x1+ where+ (fx0, xgx0) = gradWith' (,) f x0+ go x fx xgx !eta !i+ | eta == 0 = [] -- step size is 0+ | fx1 > fx = go x fx xgx (eta/2) 0 -- we stepped too far+ | zeroGrad xgx = [] -- gradient is 0+ | otherwise = x1 : if i == 10+ then go x1 fx1 xgx1 (eta*2) 0+ else go x1 fx1 xgx1 eta (i+1)+ where+ zeroGrad = all (\(_,g) -> g == 0)+ x1 = fmap (\(xi,gxi) -> xi - eta * gxi) xgx+ (fx1, xgx1) = gradWith' (,) f x1 {-# INLINE gradientDescent #-} -- | Perform a gradient descent using reverse mode automatic differentiation to compute the gradient.-gradientAscent :: (Traversable f, Fractional a, Ord a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> [f a]+gradientAscent :: (Traversable f, Fractional a, Ord a) => (forall s. Reifies s Tape => f (Reverse a s) -> Reverse a s) -> f a -> [f a] gradientAscent f = gradientDescent (negate . f) {-# INLINE gradientAscent #-} --- | Perform a conjugate gradient descent using reverse mode automatic differentiation to compute the gradient.-conjugateGradientDescent :: (Traversable f, Fractional a, Ord a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> [f a]-conjugateGradientDescent f x0 = takeWhile (all (\a -> a == a)) (go x0 d0 d0)+-- | Perform a conjugate gradient descent using reverse mode automatic differentiation to compute the gradient, and using forward-on-forward mode for computing extrema.+--+-- >>> let sq x = x * x+-- >>> let rosenbrock [x,y] = sq (1 - x) + 100 * sq (y - sq x)+-- >>> rosenbrock [0,0]+-- 1+-- >>> rosenbrock (conjugateGradientDescent rosenbrock [0, 0] !! 5) < 0.1+-- True+conjugateGradientDescent :: (Traversable f, Ord a, Fractional a) => (forall t. (Mode t, a ~ Scalar t, Num t) => f t -> t) -> f a -> [f a]+conjugateGradientDescent f = conjugateGradientAscent (negate . f)+{-# INLINE conjugateGradientDescent #-}++-- | Perform a conjugate gradient ascent using reverse mode automatic differentiation to compute the gradient.+conjugateGradientAscent :: (Traversable f, Ord a, Fractional a) => (forall t. (Mode t, a ~ Scalar t, Num t) => f t -> t) -> f a -> [f a]+conjugateGradientAscent f x0 = takeWhile (all (\a -> a == a)) (go x0 d0 d0 delta0) where dot x y = sum $ zipWithT (*) x y- d0 = negate <$> grad f x0- go xi ri di = xi : go xi1 ri1 di1+ d0 = grad f x0+ delta0 = dot d0 d0+ go xi _ri di deltai = xi : go xi1 ri1 di1 deltai1 where- ai = last $ take 20 $ extremum (\a -> f $ zipWithT (\x d -> auto x + a * auto d) xi di) 0+ ai = last $ take 20 $ extremum (\a -> f $ zipWithT (\x d -> auto x + a * auto d) xi di) 0 xi1 = zipWithT (\x d -> x + ai*d) xi di- ri1 = negate <$> grad f xi1- bi1 = max 0 $ dot ri1 (zipWithT (-) ri1 ri) / dot ri1 ri1- di1 = zipWithT (\r d -> r * bi1*d) ri1 di-{-# INLINE conjugateGradientDescent #-}---- | Perform a conjugate gradient ascent using reverse mode automatic differentiation to compute the gradient.-conjugateGradientAscent :: (Traversable f, Fractional a, Ord a) => (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> [f a]-conjugateGradientAscent f = conjugateGradientDescent (negate . f)+ ri1 = grad f xi1+ deltai1 = dot ri1 ri1+ bi1 = deltai1 / deltai+ di1 = zipWithT (\r d -> r + bi1 * d) ri1 di {-# INLINE conjugateGradientAscent #-}
− src/Numeric/AD/Types.hs
@@ -1,50 +0,0 @@-{-# LANGUAGE Rank2Types #-}--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Types--- Copyright : (c) Edward Kmett 2010-12--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : GHC only----------------------------------------------------------------------------------module Numeric.AD.Types- (- -- * AD modes- Mode(..)- -- * AD variables- , AD(..)- -- * Jets- , Jet(..)- , headJet- , tailJet- , jet- -- * Apply functions that use 'lift'- , lowerUU, lowerUF, lowerFU, lowerFF- ) where--import Numeric.AD.Internal.Identity-import Numeric.AD.Internal.Types-import Numeric.AD.Internal.Jet-import Numeric.AD.Internal.Classes---- | Evaluate a scalar-to-scalar function in the trivial identity AD mode.-lowerUU :: (forall s. Mode s => AD s a -> AD s a) -> a -> a-lowerUU f = unprobe . f . probe-{-# INLINE lowerUU #-}---- | Evaluate a scalar-to-nonscalar function in the trivial identity AD mode.-lowerUF :: (forall s. Mode s => AD s a -> f (AD s a)) -> a -> f a-lowerUF f = unprobed . f . probe-{-# INLINE lowerUF #-}---- | Evaluate a nonscalar-to-scalar function in the trivial identity AD mode.-lowerFU :: (forall s. Mode s => f (AD s a) -> AD s a) -> f a -> a-lowerFU f = unprobe . f . probed-{-# INLINE lowerFU #-}---- | Evaluate a nonscalar-to-nonscalar function in the trivial identity AD mode.-lowerFF :: (forall s. Mode s => f (AD s a) -> g (AD s a)) -> f a -> g a-lowerFF f = unprobed . f . probed-{-# INLINE lowerFF #-}
− src/Numeric/AD/Variadic.hs
@@ -1,28 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Variadic--- Copyright : (c) Edward Kmett 2010-2012--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : non-portable------ Variadic combinators for variadic mixed-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-mode variadic gradient- Grad , vgrad, vgrad'- -- * Sparse forward mode variadic jet- , Grads, vgrads- ) where--import Numeric.AD.Variadic.Kahn-import Numeric.AD.Variadic.Sparse (Grads, vgrads)
− src/Numeric/AD/Variadic/Kahn.hs
@@ -1,26 +0,0 @@--------------------------------------------------------------------------------- |--- 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/Sparse.hs
@@ -1,26 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Numeric.AD.Variadic.Sparse--- Copyright : (c) Edward Kmett 2010-2012--- License : BSD3--- Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : non-portable------ Variadic combinators for sparse forward 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.Sparse- (- -- * Unsafe Variadic Gradient- Grad , vgrad, vgrad'- , Grads, vgrads- ) where--import Numeric.AD.Internal.Sparse
tests/doctests.hs view
@@ -14,6 +14,8 @@ : "-idist/build/autogen" : "-optP-include" : "-optPdist/build/autogen/cabal_macros.h"+ : "-optP-I"+ : "-optPinclude" : "-hide-all-packages" : map ("-package="++) deps ++ sources