hasty-hamiltonian

Speedy gradient-based traversal through parameter space.
Log | Files | Refs | README | LICENSE

commit c6d45d0031e8044eb2d20fdb6dbd95d04a9de4ec
Author: Jared Tobin <jared@jtobin.ca>
Date:   Sun,  4 Nov 2012 22:32:10 +1300

Initial commit.

Diffstat:
A.gitignore | 10++++++++++
AExamples/code/Rosenbrock_HMC.hs | 45+++++++++++++++++++++++++++++++++++++++++++++
AExamples/data/input/inits-2d.dat | 1+
ALICENSE | 30++++++++++++++++++++++++++++++
ANumeric/MCMC/Hamiltonian.hs | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 2++
Ahasty-hamiltonian.cabal | 21+++++++++++++++++++++
7 files changed, 190 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,10 @@ +working +*swp +.git +.DS_Store +dist +Setup.hs +*trace* +demos +output + diff --git a/Examples/code/Rosenbrock_HMC.hs b/Examples/code/Rosenbrock_HMC.hs @@ -0,0 +1,45 @@ +import Numeric.MCMC.Hamiltonian +import System.Random.MWC +import System.Environment +import System.Exit +import System.IO +import Numeric.AD +import Control.Monad + +target :: RealFloat a => [a] -> a +target [x0, x1] = (-1)*(5*(x1 - x0^2)^2 + 0.05*(1 - x0)^2) + +gTarget :: [Double] -> [Double] +gTarget = grad target + +main = do + args <- getArgs + when (args == []) $ do + putStrLn "(hasty-hamiltonian) Rosenbrock density " + putStrLn "Usage: ./Rosenbrock_HMC <numSteps> <nDisc> <stepSize> <inits>" + putStrLn " " + putStrLn "numSteps : Number of Markov chain iterations to run. " + putStrLn "nDisc : Number of discretizing steps to take. " + putStrLn "stepSize : Perturbation scaling parameter. " + putStrLn "inits : Filepath containing points at which to " + putStrLn " initialize the chain. " + exitSuccess + + inits <- fmap (map read . words) (readFile (args !! 3)) :: IO [Double] + + let nepochs = read (head args) :: Int + nDisc = read (args !! 1) :: Double + eps = read (args !! 2) :: Double + params = Options target gTarget nDisc eps + config = MarkovChain inits 0 + + g <- create + results <- runChain params nepochs config g + + hPutStrLn stderr $ + let nAcc = accepts results + total = nepochs + in show nAcc ++ " / " ++ show total ++ " (" ++ + show ((fromIntegral nAcc / fromIntegral total) :: Float) ++ + ") proposals accepted" + diff --git a/Examples/data/input/inits-2d.dat b/Examples/data/input/inits-2d.dat @@ -0,0 +1 @@ +5.0 40.0 diff --git a/LICENSE b/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2012, Jared Tobin + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Jared Tobin nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Numeric/MCMC/Hamiltonian.hs b/Numeric/MCMC/Hamiltonian.hs @@ -0,0 +1,81 @@ +{-# OPTIONS_GHC -Wall #-} +{-# LANGUAGE BangPatterns #-} + +module Numeric.MCMC.Hamiltonian where + +import Control.Monad.Reader +import Control.Monad.Primitive +import System.Random.MWC +import System.Random.MWC.Distributions + +-- | State of the Markov chain. Current parameter values are held in 'theta', +-- while accepts counts the number of proposals accepted. +data MarkovChain = MarkovChain { theta :: [Double] + , accepts :: {-# UNPACK #-} !Int } + +-- | Options for the chain. The target (expected to be a log density), its +-- gradient, and a step size tuning parameter. +data Options = Options { _target :: [Double] -> Double + , _gTarget :: [Double] -> [Double] + , _nLeaps :: {-# UNPACK #-} !Double + , _eps :: {-# UNPACK #-} !Double } + +-- | A result with this type has a view of the chain options. +type ViewsOptions = ReaderT Options + +-- | Display the current state. +instance Show MarkovChain where + show config = filter (`notElem` "[]") $ show (theta config) + +-- | The 'leapfrog' or Stormer-Verlet discretizer. +leapfrog :: Monad m + => [Double] + -> [Double] + -> ViewsOptions m ([Double], [Double]) +leapfrog t0 r0 = do + Options _ gTarget ndisc e <- ask + let go !t !r n + | n == 0 = (t, r) + | otherwise = let rm = zipWith (+) r (map (* (0.5*e)) (gTarget t)) + tt = zipWith (+) t (map (*e) rm) + rt = zipWith (+) rm (map (* (0.5*e)) (gTarget tt)) + in go tt rt (n - 1) + return $! go t0 r0 ndisc + +-- | Perform a Metropolis accept/reject step. +metropolisStep :: PrimMonad m + => MarkovChain + -> Gen (PrimState m) + -> ViewsOptions m MarkovChain +metropolisStep state g = do + Options target _ _ _ <- ask + let t0 = theta state + nacc = accepts state + r0 <- replicateM (length t0) (lift $ standard g) + zc <- lift $ uniformR (0 :: Double, 1 :: Double) g + (t, r) <- leapfrog t0 r0 + + let mc = if zc < min 1 (exp arRatio) + then (t, 1) + else (t0, 0) + + xs <.> ys = sum $ zipWith (*) xs ys + arRatio = target t + 0.5*(r0 <.> r0) + - target t0 - 0.5*(r <.> r) + + return $! MarkovChain (fst mc) (nacc + snd mc) + +-- | Diffuse through states. +runChain :: Options -- Options of the Markov chain. + -> Int -- Number of epochs to iterate the chain. + -> MarkovChain -- Initial state of the Markov chain. + -> Gen RealWorld -- MWC PRNG + -> IO MarkovChain -- End state of the Markov chain, wrapped in IO. +runChain params nepochs initConfig g + | nepochs == 0 = return initConfig + | otherwise = do + result <- runReaderT (metropolisStep initConfig g) params + print result + runChain params (nepochs - 1) result g + + diff --git a/README.md b/README.md @@ -0,0 +1,2 @@ +Speedy, gradient-based traversal through parameter space. + diff --git a/hasty-hamiltonian.cabal b/hasty-hamiltonian.cabal @@ -0,0 +1,21 @@ +-- Initial hasty-hamiltonian.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/ + +name: hasty-hamiltonian +version: 0.1.0.0 +synopsis: Speedy traversal through parameter space. +-- description: +homepage: http://jtobin.github.com/hasty-hamiltonian +license: BSD3 +license-file: LICENSE +author: Jared Tobin +maintainer: jared@jtobin.ca +-- copyright: +category: Numeric +build-type: Simple +cabal-version: >=1.8 + +library + exposed-modules: Numeric.MCMC.Hamiltonian + -- other-modules: + build-depends: base ==4.5.*, mtl ==2.1.*, primitive ==0.4.*, mwc-random ==0.12.*