packages feed

hbayesian-0.1.0.0: src/HBayesian/MCMC/EllipticalSlice.hs

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}

module HBayesian.MCMC.EllipticalSlice
  ( ellipticalSlice
  ) where

import           HHLO.Core.Types
import           HHLO.EDSL.Ops
import           HHLO.IR.Builder
import           HBayesian.Core
import           HBayesian.HHLO.Ops
import qualified HBayesian.HHLO.RNG as RNG

ellipticalSlice :: forall s d.
                   (KnownShape s, KnownDType d)
                => (Tensor s d -> Builder (Tensor '[] d))
                -> SimpleKernel s d
ellipticalSlice logpdf = Kernel { kernelInit = kernelInit, kernelStep = kernelStep }
  where
    kernelInit _key pos = do
      ld <- logpdf pos
      return $ State pos ld

    kernelStep key state = do
      (key1, key2) <- RNG.splitKey key
      let pos = statePosition state
      let ld = stateLogDensity state

      nu <- RNG.rngNormalF32 key1 >>= convert @s @'F32 @d

      thetaRaw <- RNG.rngUniformF32 key2 >>= convert @'[] @'F32 @d
      twoPi <- constant @'[] @d (2.0 * pi)
      theta <- tmul thetaRaw twoPi

      c <- tcos theta
      sTheta <- tsin theta
      cBC <- tbroadcast @'[] @s [] c
      sBC <- tbroadcast @'[] @s [] sTheta
      pc <- tmul pos cBC
      ns <- tmul nu sBC
      pos' <- tadd pc ns

      ld' <- logpdf pos'

      zero <- constant @'[] @d 0.0
      diff <- tsub ld' ld
      accept <- tlessThan zero diff
      acceptS <- tbroadcast @'[] @s [] accept

      newPos <- tselect acceptS pos' pos
      newLd  <- tselect accept ld' ld

      one <- constant @'[] @'I64 1
      infoAcceptProb <- texp diff
      let info = Info infoAcceptProb accept one
      return (State newPos newLd, info)