neural-0.1.0.0: src/Data/Utils/Analytic.hs
{-# OPTIONS_HADDOCK show-extensions #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-|
Module : Data.Utils.Analytic
Description : "analytic" values
Copyright : (c) Lars Brünjes, 2016
License : MIT
Maintainer : brunjlar@gmail.com
Stability : experimental
Portability : portable
This module defines the numeric type 'Analytic', which has "built in differentiation".
-}
module Data.Utils.Analytic
( Analytic
, fromDouble
, fromAnalytic
, gradient
) where
import qualified Numeric.AD.Rank1.Kahn as K
import qualified Numeric.AD.Internal.Kahn as K
-- | The numeric type 'Analytic' is a wrapper around Edward Kmett's @'K.Kahn' Double@ type.
-- Using functions from Analytics to Analytics, we automatically get numerically exact gradients.
-- An number of type 'Analytic' is conceptionally a 'Double' together with an infinitesimal component.
--
newtype Analytic = Analytic { toKahn :: K.Kahn Double }
deriving (Show, Num, Eq, Floating, Fractional, Ord, Real, RealFloat, RealFrac)
-- | Converts a 'Double' to an 'Analytic' without infinitesimal component.
--
fromDouble :: Double -> Analytic
fromDouble = Analytic . K.auto
-- | Tries to convert an 'Analytic' to a 'Double'.
-- This conversion will work if the 'Analytic' has no infinitesimal component.
--
fromAnalytic :: Analytic -> Maybe Double
fromAnalytic x = case toKahn x of
K.Kahn (K.Lift y) -> Just y
_ -> Nothing
-- | Computes the gradient of an analytic function and combines it with the argument.
--
-- >>> gradient (\_ d -> d) (\[x, y] -> x * x + 3 * y + 7) [2, 1]
-- (14.0,[4.0,3.0])
--
gradient :: Traversable t
=> (Double -> Double -> a) -- ^ how to combine argument and gradient
-> (t Analytic -> Analytic) -- ^ analytic function
-> t Double -- ^ function argument
-> (Double, t a) -- ^ function value and combination of argument and gradient
gradient c f = K.gradWith' c f' where
f' = toKahn . f . fmap Analytic