hanalyze-0.2.0.0: src/Hanalyze/Optim/Numeric.hs
-- |
-- Module : Hanalyze.Optim.Numeric
-- Description : 数値勾配 (有限差分法)
-- Copyright : (c) 2026 Aelysce Project (Toshiaki Honda)
-- License : BSD-3-Clause
--
-- Numeric gradients (finite differences).
--
-- For situations where automatic differentiation is impractical (e.g. GP
-- log-marginal likelihood whose @det@ is computed inside hmatrix and would
-- be cumbersome to AD-ify).
--
-- * 'numGradCentral' — central differences (error @O(h²)@; recommended).
-- * 'numGradForward' — forward differences (error @O(h)@; half the cost).
-- * 'numHessianCentral' — Hessian approximation via central differences.
{-# LANGUAGE OverloadedStrings #-}
module Hanalyze.Optim.Numeric
( numGradCentral
, numGradForward
, numHessianCentral
) where
-- | Central-difference gradient.
--
-- @∂f/∂x_i ≈ (f(x + h e_i) − f(x − h e_i)) / (2h)@.
numGradCentral :: Double -- ^ Step size @h@.
-> ([Double] -> Double) -- ^ Objective @f@.
-> [Double] -> [Double]
numGradCentral h f x =
[ (f (set i (x !! i + h)) - f (set i (x !! i - h))) / (2 * h)
| i <- [0 .. length x - 1] ]
where
set i v = take i x ++ [v] ++ drop (i + 1) x
-- | One-sided forward-difference gradient (half the cost of
-- 'numGradCentral'):
--
-- @∂f/∂x_i ≈ (f(x + h e_i) − f(x)) / h@.
numGradForward :: Double -> ([Double] -> Double) -> [Double] -> [Double]
numGradForward h f x =
let fx = f x
in [ (f (set i (x !! i + h)) - fx) / h
| i <- [0 .. length x - 1] ]
where
set i v = take i x ++ [v] ++ drop (i + 1) x
-- | Hessian approximation by mixed forward differences.
--
-- @∂²f/∂x_i∂x_j ≈ [f(x+h eᵢ+h eⱼ) − f(x+h eᵢ) − f(x+h eⱼ) + f(x)] / h²@.
--
-- Forward-only, so accuracy is @O(h)@. The fully central variant would
-- be more accurate at four times the cost.
numHessianCentral :: Double -> ([Double] -> Double) -> [Double] -> [[Double]]
numHessianCentral h f x =
[ [ second i j | j <- [0 .. n - 1] ]
| i <- [0 .. n - 1] ]
where
n = length x
set k v = take k x ++ [v] ++ drop (k + 1) x
setBoth i j vi vj =
let x1 = set i vi
in take j x1 ++ [vj] ++ drop (j + 1) x1
fx = f x
second i j =
let f_ij = f (setBoth i j (x !! i + h) (x !! j + h))
f_i = f (set i (x !! i + h))
f_j = f (set j (x !! j + h))
in (f_ij - f_i - f_j + fx) / (h * h)