hbayesian-0.1.0.0: examples/GaussianProcessEllipticalSlice.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
-- | Example 2: Gaussian Process Regression with Elliptical Slice Sampling.
--
-- We use a tiny n=3 dataset with an identity covariance prior
-- (K = I, so K^{-1} = I) to avoid needing a Cholesky decomposition
-- in the Builder. The example still demonstrates ESS on a Gaussian
-- prior, which is the sampler's natural habitat.
module GaussianProcessEllipticalSlice
( yData
, gpLogPdf
, makeKernel
, renderStepMlir
, runChain
, runChainV2
) where
import Data.Word (Word64)
import Data.Text (Text)
import HHLO.Core.Types
import HHLO.IR.AST (FuncArg(..), TensorType(..))
import HHLO.IR.Builder
import HBayesian.Core
import HBayesian.HHLO.Ops
import HBayesian.HHLO.PJRT
import HBayesian.MCMC.EllipticalSlice
import HBayesian.Chain
import Common
-- | Observed responses (n = 3).
yData :: [Float]
yData = [0.5, 2.0, 3.5]
-- | GP log-posterior with identity prior covariance.
gpLogPdf :: Tensor '[3] 'F32 -> Builder (Tensor '[] 'F32)
gpLogPdf f = do
y0 <- tconstant @'[] @'F32 (realToFrac (yData !! 0))
y1 <- tconstant @'[] @'F32 (realToFrac (yData !! 1))
y2 <- tconstant @'[] @'F32 (realToFrac (yData !! 2))
f0 <- tslice1 @3 @'F32 f 0
f1 <- tslice1 @3 @'F32 f 1
f2 <- tslice1 @3 @'F32 f 2
d0 <- tsub y0 f0
d1 <- tsub y1 f1
d2 <- tsub y2 f2
d0sq <- tmul d0 d0
d1sq <- tmul d1 d1
d2sq <- tmul d2 d2
negHalf <- tconstant @'[] @'F32 (-0.5)
llh0 <- tmul negHalf d0sq
llh1 <- tmul negHalf d1sq
llh2 <- tmul negHalf d2sq
llh01 <- tadd llh0 llh1
llh <- tadd llh01 llh2
f0sq <- tmul f0 f0
f1sq <- tmul f1 f1
f2sq <- tmul f2 f2
fSqSum <- tadd f0sq =<< tadd f1sq f2sq
prior <- tmul negHalf fSqSum
tadd llh prior
-- | Factory: build an Elliptical Slice kernel for this model.
makeKernel :: SimpleKernel '[3] 'F32
makeKernel = ellipticalSlice gpLogPdf
-- | Tier A: render one kernel step to MLIR text.
renderStepMlir :: Text
renderStepMlir =
renderKernelStep @'[3] @'F32
[ FuncArg "key" (TensorType [2] UI64)
, FuncArg "pos" (TensorType [3] F32)
, FuncArg "ld" (TensorType [] F32)
] $ do
key <- arg @'[2] @'UI64
pos <- arg @'[3] @'F32
ld <- arg @'[] @'F32
(state', _info) <- kernelStep makeKernel (Key key) (State pos ld)
return (statePosition state')
-- | Tier B: run a short chain on PJRT and return the sampled latent vectors.
runChain :: IO [[Float]]
runChain = withPJRTCPU $ \api client -> do
-- Compile the log-pdf module
let ldMod = moduleFromBuilder @'[] @'F32 "main"
[ FuncArg "f" (TensorType [3] F32) ] $ do
f <- arg @'[3] @'F32
gpLogPdf f
ldExe <- compileModule api client ldMod
-- Compile the kernel-step module (single result: position)
let stepMod = moduleFromBuilder @'[3] @'F32 "main"
[ FuncArg "key" (TensorType [2] UI64)
, FuncArg "pos" (TensorType [3] F32)
, FuncArg "ld" (TensorType [] F32)
] $ do
key <- arg @'[2] @'UI64
pos <- arg @'[3] @'F32
ld <- arg @'[] @'F32
(state', _info) <- kernelStep makeKernel (Key key) (State pos ld)
return (statePosition state')
stepExe <- compileModule api client stepMod
let seed :: Word64 = 42
f0 = [0.0, 0.0, 0.0]
-- Compute initial log-density
fBuf0 <- bufferFromF32 api client [3] f0
[ldBuf0] <- executeModule api ldExe [fBuf0]
[ld0] <- bufferToF32 api ldBuf0 1
loop api client stepExe ldExe seed (0 :: Int) f0 ld0 (10 :: Int) []
where
loop _ _ _ _ _ _ _ _ 0 acc = return (reverse acc)
loop api client stepExe ldExe seed step pos ld n acc = do
let key = [seed, fromIntegral step]
keyBuf <- bufferFromUI64 api client [2] key
posBuf <- bufferFromF32 api client [3] pos
ldBuf <- bufferFromF32 api client [] [ld]
[newPosBuf] <- executeModule api stepExe [keyBuf, posBuf, ldBuf]
newPos <- bufferToF32 api newPosBuf 3
[newLdBuf] <- executeModule api ldExe [newPosBuf]
[newLd] <- bufferToF32 api newLdBuf 1
loop api client stepExe ldExe seed (step + 1) newPos newLd (n - 1) (newPos : acc)
-- | v0.2: Run a chain using the 'Chain' combinators.
runChainV2 :: IO ([[Float]], [Diagnostic])
runChainV2 = do
let ck = compileSimpleKernel makeKernel gpLogPdf
sampleChain ck [0.0, 0.0, 0.0] $ defaultChainConfig
{ ccNumIterations = 10
, ccSeed = 42
}