packages feed

ad-4.5: src/Numeric/AD/Internal/Tower/Double.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
{-# OPTIONS_HADDOCK not-home #-}

-----------------------------------------------------------------------------
-- |
-- Copyright   : (c) Edward Kmett 2010-2021
-- License     : BSD3
-- Maintainer  : ekmett@gmail.com
-- Stability   : experimental
-- Portability : GHC only
--
-----------------------------------------------------------------------------

#ifndef MIN_VERSION_base
#define MIN_VERSION_base(x,y,z) 1
#endif

module Numeric.AD.Internal.Tower.Double
  ( TowerDouble(..)
  , List(..)
  , zeroPad
  , zeroPadF
  , transposePadF
  , d, dl
  , d', dl'
  , withD
  , tangents
  , bundle
  , apply
  , getADTower
  , tower
  ) where

import Prelude hiding (all, sum)
import Control.Monad (join)
import Data.Foldable
import Data.Data (Data)
import Data.Number.Erf
import Data.Typeable (Typeable)
import Numeric.AD.Internal.Combinators
import Numeric.AD.Jacobian
import Numeric.AD.Mode
import Text.Read
import GHC.Exts as Exts (IsList(..))
#if !(MIN_VERSION_base(4,11,0))
import Data.Semigroup (Semigroup(..))
#endif

-- spine lazy, value strict list of doubles
data List
  = Nil
  | {-# UNPACK #-} !Double :! List
  deriving (Eq,Ord,Typeable,Data)

infixr 5 :!


instance Semigroup List where
  Nil <> xs = xs
  (x :! xs) <> ys = x :! (xs <> ys)

instance Monoid List where
  mempty = Nil
  mappend = (<>)

instance IsList List where
  type Item List = Double
  toList Nil = []
  toList (a :! as) = a : Exts.toList as
  fromList [] = Nil
  fromList (a : as) = a :! Exts.fromList as

instance Show List where
  showsPrec d = showsPrec d . Exts.toList

instance Read List where
  readPrec = Exts.fromList <$> step readPrec

lmap :: (Double -> Double) -> List -> List
lmap f (a :! as) = f a :! lmap f as
lmap _ Nil = Nil


-- | @Tower@ is an AD 'Mode' that calculates a tangent tower by forward AD, and provides fast 'diffsUU', 'diffsUF'
newtype TowerDouble = Tower { getTower :: List }
  deriving (Data, Typeable)

instance Show TowerDouble where
  showsPrec n (Tower as) = showParen (n > 10) $ showString "Tower " . showsPrec 11 as

-- Local combinators

zeroPad :: Num a => [a] -> [a]
zeroPad xs = xs ++ repeat 0
{-# INLINE zeroPad #-}

zeroPadF :: (Functor f, Num a) => [f a] -> [f a]
zeroPadF fxs@(fx:_) = fxs ++ repeat (0 <$ fx)
zeroPadF _ = error "zeroPadF :: empty list"
{-# INLINE zeroPadF #-}

lnull :: List -> Bool
lnull Nil = True
lnull _ = False

transposePadF :: (Foldable f, Functor f) => Double -> f List -> [f Double]
transposePadF pad fx
  | all lnull fx = []
  | otherwise = fmap headPad fx : transposePadF pad (drop1 <$> fx)
  where
    headPad Nil = pad
    headPad (x :! _) = x
    drop1 (_ :! xs) = xs
    drop1 xs = xs

d :: Num a => [a] -> a
d (_:da:_) = da
d _ = 0
{-# INLINE d #-}

dl :: List -> Double
dl (_ :! da :! _) = da
dl _ = 0
{-# INLINE dl #-}

d' :: Num a => [a] -> (a, a)
d' (a:da:_) = (a, da)
d' (a:_)    = (a, 0)
d' _        = (0, 0)
{-# INLINE d' #-}

dl' :: List -> (Double, Double)
dl' (a:!da:!_) = (a, da)
dl' (a:!_)     = (a, 0)
dl' _          = (0, 0)
{-# INLINE dl' #-}

tangents :: TowerDouble -> TowerDouble
tangents (Tower Nil) = Tower Nil
tangents (Tower (_ :! xs)) = Tower xs
{-# INLINE tangents #-}

truncated :: TowerDouble -> Bool
truncated (Tower Nil) = True
truncated _ = False
{-# INLINE truncated #-}

bundle :: Double -> TowerDouble -> TowerDouble
bundle a (Tower as) = Tower (a :! as)
{-# INLINE bundle #-}

withD :: (Double, Double) -> TowerDouble
withD (a, da) = Tower (a :! da :! Nil)
{-# INLINE withD #-}

apply :: (TowerDouble -> b) -> Double -> b
apply f a = f (Tower (a :! 1 :! Nil))
{-# INLINE apply #-}

getADTower :: TowerDouble -> [Double]
getADTower = Exts.toList . getTower
{-# INLINE getADTower #-}

tower :: [Double] -> TowerDouble
tower = Tower . Exts.fromList

primal :: TowerDouble -> Double
primal (Tower (x:!_)) = x
primal _ = 0

instance Mode TowerDouble where
  type Scalar TowerDouble = Double

  auto a = Tower (a :! Nil)

  isKnownZero (Tower Nil) = True
  isKnownZero (Tower (0 :! Nil)) = True
  isKnownZero _ = False

  asKnownConstant (Tower Nil) = Just 0
  asKnownConstant (Tower (a :! Nil)) = Just a
  asKnownConstant Tower {} = Nothing

  isKnownConstant (Tower Nil) = True
  isKnownConstant (Tower (_ :! Nil)) = True
  isKnownConstant Tower {} = False

  zero = Tower Nil

  a *^ Tower bs = Tower (lmap (a*) bs)

  Tower as ^* b = Tower (lmap (*b) as)

  Tower as ^/ b = Tower (lmap (/b) as)

infixr 6 <+>

(<+>) :: TowerDouble -> TowerDouble -> TowerDouble
Tower Nil <+> bs = bs
as <+> Tower Nil = as
Tower (a:!as) <+> Tower (b:!bs) = Tower (c:!cs) where
  c = a + b
  Tower cs = Tower as <+> Tower bs

instance Jacobian TowerDouble where
  type D TowerDouble = TowerDouble
  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)

  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

lzipWith :: (Double -> Double -> Double) -> List -> List -> List
lzipWith f (a :! as) (b :! bs) = f a b :! lzipWith f as bs
lzipWith _ _ _ = Nil

lsumProd3 :: List -> List -> List -> Double
lsumProd3 as0 bs0 cs0 = go as0 bs0 cs0 0 where
  go (a :! as) (b :! bs) (c :! cs) !acc = go as bs cs (a*b*c + acc)
  go _ _ _ acc = acc;

ltail :: List -> List
ltail (_ :! as) = as
ltail _ = error "ltail"

-- mul xs ys = [ sum [xs!!j * ys!!(k-j)*bin k j | j <- [0..k]] | k <- [0..] ]
-- adapted for efficiency and to handle finite lists xs, ys
mul:: TowerDouble -> TowerDouble -> TowerDouble
mul (Tower Nil) _ = Tower Nil
mul (Tower (a :! as)) (Tower bs) = Tower (convs' (1 :! Nil) (a :! Nil) as bs)
  where convs' _ _ _ Nil = Nil
        convs' ps ars as bs = lsumProd3 ps ars bs :!
              case as of
                 Nil -> convs'' (next' ps) ars bs
                 a:!as -> convs' (next ps) (a:!ars) as bs
        convs'' _ _ Nil = undefined -- convs'' never called with last argument empty
        convs'' _ _ (_:! Nil) = Nil
        convs'' ps ars (_:!bs) = lsumProd3 ps ars bs :! convs'' (next' ps) ars bs
        next xs = 1 :! lzipWith (+) xs (ltail xs) <> (1 :! Nil) -- next row in Pascal's triangle
        next' xs = lzipWith (+) xs (ltail xs) <> (1 :! Nil) -- end part of next row in Pascal's triangle

#define HEAD TowerDouble
#define BODY1(x)
#define BODY2(x,y)
#define NO_Bounded
#include <instances.h>