nonlinear-optimization-ad-0.2.4: src/Numeric/Optimization/Algorithms/HagerZhang05/AD.hs
{-# LANGUAGE ScopedTypeVariables, Rank2Types, FlexibleContexts, CPP #-}
{-# OPTIONS_GHC -Wall #-}
--
-----------------------------------------------------------------------------
-- |
-- Module : Numeric.Optimization.Algorithms.HagerZhang05.AD
-- Copyright : (c) Masahiro Sakai 2013
-- License : GPL
--
-- Maintainer : masahiro.sakai@gmail.com
-- Stability : experimental
-- Portability : non-portable
--
-- This package enhance
-- [nonlinear-optimization](https://hackage.haskell.org/package/nonlinear-optimization)'s
-- usability by using
-- [ad](https://hackage.haskell.org/package/nonlinear-optimization)'s
-- automatic differentiaion. You only need to specify a function to
-- minimize and don't need to specify its gradient explicitly.
-----------------------------------------------------------------------------
module Numeric.Optimization.Algorithms.HagerZhang05.AD
( -- * Main function
optimize
-- * Result and statistics
, Result(..)
, Statistics(..)
-- * Options
, defaultParameters
, Parameters(..)
, Verbose(..)
, LineSearch(..)
, StopRules(..)
, EstimateError(..)
-- * Technical parameters
, TechParameters(..)
-- * Re-export
, Mode (..)
) where
import Prelude hiding (mapM)
import Control.Monad.Primitive
import Data.Foldable (foldlM)
import Data.Traversable (Traversable (..), mapAccumL, mapM)
import qualified Data.Vector.Storable as S
import qualified Data.Vector.Storable.Mutable as SM
import Numeric.AD
#if MIN_VERSION_ad(4,0,0)
import Data.Reflection (Reifies)
import Numeric.AD.Mode.Reverse
import Numeric.AD.Internal.Reverse (Tape)
#else
import Numeric.AD.Types
#endif
import Numeric.Optimization.Algorithms.HagerZhang05 hiding (optimize)
import qualified Numeric.Optimization.Algorithms.HagerZhang05 as HagerZhang05
{-# INLINE optimize #-}
-- | Run the @CG_DESCENT@ optimizer and try to minimize the function.
--
-- It uses reverse mode automatic differentiation to compute the gradient.
optimize
:: forall f. Traversable f
=> Parameters -- ^ How should we optimize.
-> Double -- ^ @grad_tol@, see 'stopRules'.
-> f Double -- ^ Initial guess.
#if MIN_VERSION_ad(4,0,0)
-- -> (forall s. (Mode s, Scalar s ~ Double) => f s -> s) -- ^ Function to be minimized.
-> (forall s. Reifies s Tape => f (Reverse s Double) -> Reverse s Double) -- ^ Function to be minimized.
#else
-> (forall s. Mode s => f (AD s Double) -> AD s Double) -- ^ Function to be minimized.
#endif
-> IO (f Double, Result, Statistics)
optimize params grad_tol initial f = do
let size :: Int
template :: f Int
(size, template) = mapAccumL (\i _ -> i `seq` (i+1, i)) 0 initial
readFromMVec :: PrimMonad m => SM.MVector (PrimState m) Double -> m (f Double)
readFromMVec mx = mapM (SM.read mx) template
writeToMVec :: PrimMonad m => f Double -> SM.MVector (PrimState m) Double -> m ()
writeToMVec x mx = do
_ <- foldlM (\i v -> SM.write mx i v >> return (i+1)) 0 x
return ()
readFromVec :: S.Vector Double -> f Double
readFromVec x = fmap (x S.!) template
mf :: forall m. PrimMonad m => PointMVector m -> m Double
mf mx = do
x <- readFromMVec mx
#if MIN_VERSION_ad(4,0,0)
return $ fst $ grad' f x
#else
return $ lowerFU f x
#endif
mg :: forall m. PrimMonad m => PointMVector m -> GradientMVector m -> m ()
mg mx mret = do
x <- readFromMVec mx
writeToMVec (grad f x) mret
mc :: (forall m. PrimMonad m => PointMVector m -> GradientMVector m -> m Double)
mc mx mret = do
x <- readFromMVec mx
let (y,g) = grad' f x
writeToMVec g mret
return y
vx0 :: S.Vector Double
vx0 = S.create $ do
mx <- SM.new size
writeToMVec initial mx
return mx
(vx, result, stat) <- HagerZhang05.optimize params grad_tol vx0 (MFunction mf) (MGradient mg) (Just (MCombined mc))
return (readFromVec vx, result, stat)