srtree-3.0.0.0: src/Algorithm/SRTree/NonlinearOpt.hs
{-# LANGUAGE BangPatterns #-}
-----------------------------------------------------------------------------
-- |
-- Module : Algorithm.SRTree.Opt
-- Copyright : (c) Fabricio Olivetti 2021 - 2024
-- License : BSD3
-- Maintainer : fabricio.olivetti@gmail.com
-- Stability : experimental
-- Portability : ConstraintKinds
--
-- Functions to optimize the parameters of an expression.
--
-----------------------------------------------------------------------------
module Algorithm.SRTree.NonlinearOpt
where
import Algorithm.SRTree.Likelihoods
import Numeric.Optimization.NLOPT
import Data.Bifunctor (bimap, second)
import Data.SRTree (Fix (..), SRTree (..), floatConstsToParam, relabelParams, countNodes, convertProtectedOps)
import Data.SRTree.Eval
import Algorithm.SRTree.AD
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Unboxed.Mutable as VM
import qualified Data.Vector.Generic as G
import qualified Data.IntMap.Strict as IntMap
import Data.SRTree.Recursion
import Control.Monad.State.Strict
import Control.Monad.Identity
import Debug.Trace
minimizeNLLWith :: (VS.Vector Double -> (Double, VS.Vector Double)) -> (ObjectiveD -> (Maybe VectorStorage) -> LocalAlgorithm) -> Int -> Target -> (Target, Double, Int)
minimizeNLLWith funAndGrad alg niter t0
| niter == 0 = (t0, f, 0)
| n == 0 = (t0, f, 0)
| otherwise = (t_opt', fst (funAndGrad t_opt), nEvs)
where
t0' = G.convert t0
n = V.length t0
(f, _) = funAndGrad t0' -- if there's no parameter or no iterations
algorithm = alg funAndGrad (Just $ VectorStorage $ fromIntegral n)
stop = ObjectiveRelativeTolerance 1e-6 :| [ObjectiveAbsoluteTolerance 1e-6, MaximumEvaluations (fromIntegral niter)]
problem = LocalProblem (fromIntegral n) stop algorithm
(t_opt, nEvs) = case minimizeLocal problem t0' of
Right sol -> (solutionParams sol, nEvals sol)
Left e -> (t0', 0)
t_opt' = G.convert t_opt
{-# INLINE minimizeNLLWith #-}
-- | minimizes the negative log-likelihood of the expression
minimizeNLL' :: (ObjectiveD -> (Maybe VectorStorage) -> LocalAlgorithm) -> ADBackEnd -> Loss -> Maybe Target -> Int -> Columns -> Target -> Fix SRTree -> Target -> (Target, Double, Int)
minimizeNLL' alg backend dist mYerr niter xss ys tree t0 = minimizeNLLWith funAndGrad alg niter t0
where
m = V.length ys
tree' = buildLoss dist (fromIntegral m) tree
funAndGrad = compileFunAndGrad backend xss ys mYerr tree'
minimizeNLL :: ADBackEnd -> Loss -> Maybe Target -> Int -> Columns -> Target -> Fix SRTree -> Target -> (Target, Double, Int)
minimizeNLL = minimizeNLL' TNEWTON
minimizeNLLWithFixedParam' :: (ObjectiveD -> (Maybe VectorStorage) -> LocalAlgorithm) -> ADBackEnd -> Loss -> Maybe Target -> Int -> Columns -> Target -> Fix SRTree -> Int -> Target -> Target
minimizeNLLWithFixedParam' alg backend dist mYerr' niter xss' ys' tree ix t0 = result
where
m = V.length ys'
tree' = buildLoss dist (fromIntegral m) tree
fixedVal = t0 V.! ix
p = V.length t0
evalFull = compileFunAndGrad backend xss' ys' mYerr' tree'
wrapRed thRed = let (lo, hi) = VS.splitAt ix thRed
in (lo `VS.snoc` fixedVal) VS.++ hi
unwrapRed th = let (lo, hi) = VS.splitAt ix th
in lo VS.++ VS.tail hi
wrap thRed = let (lo, hi) = V.splitAt ix thRed in (lo `V.snoc` fixedVal) V.++ hi
unwrap th = let (lo, hi) = V.splitAt ix th in lo V.++ V.tail hi
fgRed :: VS.Vector Double -> (Double, VS.Vector Double)
fgRed thRed =
let thFull = wrapRed thRed
(nll, gradFull) = evalFull thFull
gradRed = unwrapRed gradFull
in (nll, gradRed)
t0Red = unwrap t0
(tRawRed,_,_) = minimizeNLLWith fgRed alg niter t0Red
result = wrap tRawRed
minimizeNLLWithFixedParam = minimizeNLLWithFixedParam' TNEWTON