packages feed

mcmc 0.2.2 → 0.2.3

raw patch · 21 files changed

+376/−174 lines, 21 filesdep −asyncPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: async

API changes (from Hackage documentation)

- Mcmc.Mcmc: mcmcMonitorStdOutHeader :: Mcmc a ()
- Mcmc.Monitor: mHeader :: Monitor a -> ByteString
- Mcmc.Proposal: getNCycles :: Cycle a -> Int -> GenIO -> IO [[Proposal a]]
- Mcmc.Proposal.Generic: proposalGenericContinuous :: (ContDistr d, ContGen d) => d -> (a -> Double -> a) -> Maybe (Double -> Double) -> ProposalSimple a
- Mcmc.Proposal.Generic: proposalGenericDiscrete :: (DiscreteDistr d, DiscreteGen d) => d -> (a -> Int -> a) -> Maybe (Int -> Int) -> ProposalSimple a
+ Mcmc.Prior: exponential :: Double -> Double -> Log Double
+ Mcmc.Prior: gamma :: Double -> Double -> Double -> Log Double
+ Mcmc.Prior: negative :: Double -> Log Double
+ Mcmc.Prior: normal :: Double -> Double -> Double -> Log Double
+ Mcmc.Prior: positive :: Double -> Log Double
+ Mcmc.Prior: product' :: [Log Double] -> Log Double
+ Mcmc.Prior: uniform :: Double -> Double -> Double -> Log Double
+ Mcmc.Proposal: getNIterations :: Cycle a -> Int -> GenIO -> IO [[Proposal a]]
+ Mcmc.Proposal.Generic: genericContinuous :: (ContDistr d, ContGen d) => d -> (a -> Double -> a) -> Maybe (Double -> Double) -> ProposalSimple a
+ Mcmc.Proposal.Generic: genericDiscrete :: (DiscreteDistr d, DiscreteGen d) => d -> (a -> Int -> a) -> Maybe (Int -> Int) -> ProposalSimple a
+ Mcmc.Proposal.Scale: scaleContrarily :: String -> Int -> Double -> Double -> Bool -> Proposal (Double, Double)
+ Mcmc.Proposal.Slide: slideContrarily :: String -> Int -> Double -> Double -> Bool -> Proposal (Double, Double)
- Mcmc.Monitor.Parameter: (@.) :: Lens' b a -> MonitorParameter a -> MonitorParameter b
+ Mcmc.Monitor.Parameter: (@.) :: (b -> a) -> MonitorParameter a -> MonitorParameter b
- Mcmc.Monitor.ParameterBatch: (@#) :: Lens' b a -> MonitorParameterBatch a -> MonitorParameterBatch b
+ Mcmc.Monitor.ParameterBatch: (@#) :: (b -> a) -> MonitorParameterBatch a -> MonitorParameterBatch b

Files

ChangeLog.md view
@@ -5,6 +5,20 @@ ## Unreleased changes  +## 0.2.3++-   Contrary proposals.+-   Change how monitors are lifted (use normal function, not a lens).+-   Priors.+-   Remove concurrent monitors (was slow).+-   Improve MCMC sampler output.+++## 0.2.2++-   Move away from hpack.++ ## 0.2.1  -   Consistently use ByteString instead of Text.
bench/Poisson.hs view
@@ -60,10 +60,10 @@ initial = (0, 0)  monAlpha :: MonitorParameter I-monAlpha = _1 @. monitorDouble "alpha"+monAlpha = fst @. monitorDouble "alpha"  monBeta :: MonitorParameter I-monBeta = _2 @. monitorDouble "beta"+monBeta = snd @. monitorDouble "beta"  monStd :: MonitorStdOut I monStd = monitorStdOut [monAlpha, monBeta] 150
mcmc.cabal view
@@ -1,6 +1,6 @@ cabal-version:  2.2 name:           mcmc-version:        0.2.2+version:        0.2.3 synopsis:       Sample from a posterior using Markov chain Monte Carlo description:    Please see the README on GitHub at <https://github.com/dschrempf/mcmc#readme> category:       Math, Statistics@@ -32,6 +32,7 @@       Mcmc.Monitor.Parameter       Mcmc.Monitor.ParameterBatch       Mcmc.Monitor.Time+      Mcmc.Prior       Mcmc.Proposal       Mcmc.Proposal.Bactrian       Mcmc.Proposal.Generic@@ -51,7 +52,6 @@   ghc-options: -Wall   build-depends:       aeson-    , async     , base >=4.7 && <5     , bytestring     , containers
src/Mcmc.hs view
@@ -71,7 +71,7 @@     --     -- @     -- slideSimple :: Lens' a Double -> Double -> Double -> Double -> ProposalSimple a-    -- slideSimple l m s t = proposalGenericContinuous l (normalDistr m (s * t)) (+) (-)+    -- slideSimple l m s t = genericContinuous l (normalDistr m (s * t)) (+) (-)     -- @     --     -- This specification is more involved. Especially since we need to know the@@ -133,6 +133,9 @@     module Mcmc.Monitor.Parameter,     module Mcmc.Monitor.ParameterBatch, +    -- * Prior distributions+    module Mcmc.Prior,+     -- * Algorithms      -- | At the moment, the library is tailored to the Metropolis-Hastings@@ -150,6 +153,7 @@ import Mcmc.Monitor import Mcmc.Monitor.Parameter import Mcmc.Monitor.ParameterBatch+import Mcmc.Prior import Mcmc.Proposal import Mcmc.Proposal.Bactrian import Mcmc.Proposal.Scale
src/Mcmc/Item.hs view
@@ -30,15 +30,14 @@ -- | An 'Item', or link of the Markov chain. For reasons of computational -- efficiency, each state is associated with the corresponding prior and -- likelihood.-data Item a-  = Item-      { -- | The current state in the state space @a@.-        state :: a,-        -- | The current prior.-        prior :: Log Double,-        -- | The current likelihood.-        likelihood :: Log Double-      }+data Item a = Item+  { -- | The current state in the state space @a@.+    state :: a,+    -- | The current prior.+    prior :: Log Double,+    -- | The current likelihood.+    likelihood :: Log Double+  }   deriving (Eq, Ord, Show, Read)  instance ToJSON a => ToJSON (Item a) where
src/Mcmc/Mcmc.hs view
@@ -25,7 +25,6 @@     mcmcResetA,     mcmcSummarizeCycle,     mcmcReport,-    mcmcMonitorStdOutHeader,     mcmcMonitorExec,     mcmcRun,   )@@ -183,14 +182,7 @@     Nothing -> return ()   mcmcInfoS $ "Run chain for " <> show n <> " iterations."   mcmcInfoT "Initial state."-  mcmcMonitorStdOutHeader   mcmcMonitorExec---- | Print header line of standard output monitor.-mcmcMonitorStdOutHeader :: Mcmc a ()-mcmcMonitorStdOutHeader = do-  m <- gets monitor-  mcmcInfoA $ mcmcOutT $ mHeader m  -- Save the status of an MCMC run. See 'saveStatus'. mcmcSave :: ToJSON a => Mcmc a ()
src/Mcmc/Metropolis.hs view
@@ -32,10 +32,20 @@ import System.Random.MWC import Prelude hiding (cycle) --- For non-symmetric proposals.+-- The Metropolis-Hastings ratio. ----- q = qYX / qXY+-- 'Infinity' if fX is zero. In this case, the proposal is always accepted.+--+-- 'NaN' if (fY or q) and fX are zero. In this case, the proposal is always+-- rejected.++-- There is a discrepancy between authors saying that one should (a) always+-- accept the new state when the current posterior is zero (Chapter 4 of the+-- Handbook of Markov Chain Monte Carlo), or (b) almost surely reject the+-- proposal when either fY or q are zero (Chapter 1). Since I trust the author+-- of Chapter 1 (Charles Geyer) I choose to follow option (b). mhRatio :: Log Double -> Log Double -> Log Double -> Log Double+-- q = qYX / qXY mhRatio fX fY q = fY * q / fX {-# INLINE mhRatio #-} @@ -82,7 +92,7 @@   mcmcDebugS $ "Run " <> show n <> " iterations."   c <- gets cycle   g <- gets generator-  cycles <- liftIO $ getNCycles c n g+  cycles <- liftIO $ getNIterations c n g   forM_ cycles mhIter  -- Burn in and auto tune.@@ -91,14 +101,12 @@   | t <= 0 = error "mhBurnInN: Auto tuning period smaller equal 0."   | b > t = do     mcmcResetA-    mcmcMonitorStdOutHeader     mhNIter t     mcmcSummarizeCycle >>= mcmcDebugT     mcmcAutotune     mhBurnInN (b - t) (Just t)   | otherwise = do     mcmcResetA-    mcmcMonitorStdOutHeader     mhNIter b     mcmcSummarizeCycle >>= mcmcInfoT     mcmcInfoS $ "Acceptance ratios calculated over the last " <> show b <> " iterations."@@ -120,20 +128,21 @@ mhRun n = do   mcmcResetA   mcmcInfoS $ "Run chain for " <> show n <> " iterations."-  let (m, r) = n `quotRem` 100-  -- Print header to standard output every 100 iterations.-  replicateM_ m $ do-    mcmcMonitorStdOutHeader-    mhNIter 100-  when (r > 0) $ do-    mcmcMonitorStdOutHeader-    mhNIter r+  -- let (m, r) = n `quotRem` 100+  -- -- Print header to standard output every 100 iterations.+  -- replicateM_ m $ do+  --   mcmcMonitorStdOutHeader+  --   mhNIter 100+  -- when (r > 0) $ do+  --   mcmcMonitorStdOutHeader+  --   mhNIter r+  mhNIter n  mhT :: ToJSON a => Mcmc a () mhT = do   mcmcInfoT "Metropolis-Hastings sampler."-  mcmcReport   mcmcSummarizeCycle >>= mcmcInfoT+  mcmcReport   s <- get   let b = fromMaybe 0 (burnInIterations s)   mhBurnIn b (autoTuningPeriod s)@@ -147,6 +156,14 @@   mhRun dn  -- | Continue a Markov chain for a given number of Metropolis-Hastings steps.+--+-- At the moment, when an MCMC run is continued, the old @.mcmc@ file is+-- deleted. This behavior may change in the future.+--+-- This means that an interrupted continuation also breaks previous runs. This+-- step is necessary because, otherwise, incomplete monitor files are left on+-- disk, if a continuation is canceled. Subsequent continuations would append to+-- the incomplete monitor files and produce garbage. mhContinue ::   ToJSON a =>   -- | Additional number of Metropolis-Hastings steps.
src/Mcmc/Monitor.hs view
@@ -21,16 +21,14 @@     MonitorBatch,     monitorBatch, -    -- * Use monitor+    -- * Use monitors     mOpen,     mAppend,-    mHeader,     mExec,     mClose,   ) where -import Control.Concurrent.Async import Control.Monad import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as BL@@ -101,6 +99,33 @@     sep = "   " <> BL.replicate (BL.length row - 3) '-'     nms = [BL.pack $ mpName p | p <- msParams m] +msDataLine ::+  Int ->+  Item a ->+  Int ->+  UTCTime ->+  Int ->+  MonitorStdOut a ->+  IO BL.ByteString+msDataLine i (Item x p l) ss st j m = do+  ct <- getCurrentTime+  let dt = ct `diffUTCTime` st+      -- Careful, don't evaluate this when i == ss.+      timePerIter = dt / fromIntegral (i - ss)+      -- -- Always 0; doesn't make much sense.+      -- tpi = if (i - ss) < 10+      --       then ""+      --       else renderDurationS timePerIter+      eta =+        if (i - ss) < 10+          then ""+          else renderDuration $ timePerIter * fromIntegral (j - i)+  return $+    msRenderRow $+      [BL.pack (show i), renderLog p, renderLog l, renderLog (p * l)]+        ++ [BB.toLazyByteString $ mpFunc mp x | mp <- msParams m]+        ++ [renderDuration dt, eta]+ msExec ::   Int ->   Item a ->@@ -109,27 +134,12 @@   Int ->   MonitorStdOut a ->   IO (Maybe BL.ByteString)-msExec i (Item x p l) ss st j m+msExec i it ss st j m   | i `mod` msPeriod m /= 0 = return Nothing-  | otherwise = do-    ct <- getCurrentTime-    let dt = ct `diffUTCTime` st-        -- Careful, don't evaluate this when i == ss.-        timePerIter = dt / fromIntegral (i - ss)-        -- -- Always 0; doesn't make much sense.-        -- tpi = if (i - ss) < 10-        --       then ""-        --       else renderDurationS timePerIter-        eta =-          if (i - ss) < 10-            then ""-            else renderDuration $ timePerIter * fromIntegral (j - i)-    return $-      Just $-        msRenderRow $-          [BL.pack (show i), renderLog p, renderLog l, renderLog (p * l)]-            ++ [BB.toLazyByteString $ mpFunc mp x | mp <- msParams m]-            ++ [renderDuration dt, eta]+  | i `mod` (msPeriod m * 100) == 0 = do+    l <- msDataLine i it ss st j m+    return $ Just $ msHeader m <> "\n" <> l+  | otherwise = Just <$> msDataLine i it ss st j m  -- | Monitor to a file; constructed with 'monitorFile'. data MonitorFile a = MonitorFile@@ -331,6 +341,7 @@   mapM_ mfHeader fs'   bs' <- mapM (mbOpen n frc) bs   mapM_ mbHeader bs'+  hSetBuffering stdout LineBuffering   return $ Monitor s fs' bs'  -- | Open the files associated with the 'Monitor' in append mode.@@ -340,10 +351,6 @@   bs' <- mapM (mbAppend n) bs   return $ Monitor s fs' bs' --- | Get header line of 'MonitorStdOut'.-mHeader :: Monitor a -> BL.ByteString-mHeader (Monitor s _ _) = msHeader s- -- | Execute monitors; print status information to files and return text to be -- printed to standard output and log file. mExec ::@@ -363,15 +370,11 @@   Monitor a ->   IO (Maybe BL.ByteString) mExec v i ss st xs j (Monitor s fs bs) = do-  -- XXX: I am not sure if this concurrency is necessary.-  mf <- async $ mapConcurrently_ (mfExec i $ headT xs) fs-  mb <- async $ mapConcurrently_ (mbExec i xs) bs-  ms <- async $ if v == Quiet-                then return Nothing-                else msExec i (headT xs) ss st j s-  wait mf-  wait mb-  wait ms+  mapM_ (mfExec i $ headT xs) fs+  mapM_ (mbExec i xs) bs+  if v == Quiet+    then return Nothing+    else msExec i (headT xs) ss st j s  -- | Close the files associated with the 'Monitor'. mClose :: Monitor a -> IO (Monitor a)
src/Mcmc/Monitor/Parameter.hs view
@@ -24,7 +24,6 @@  import qualified Data.ByteString.Builder as BB import qualified Data.Double.Conversion.ByteString as BC-import Lens.Micro  -- | Instruction about a parameter to monitor. data MonitorParameter a = MonitorParameter@@ -34,15 +33,15 @@     mpFunc :: a -> BB.Builder   } --- | Convert a parameter monitor from one data type to another using a lens.+-- | Convert a parameter monitor from one data type to another. -- -- For example, to monitor a 'Double' value being the first entry of a tuple: -- -- @--- mon = _1 @@ monitorDouble+-- mon = fst @. monitorDouble -- @-(@.) :: Lens' b a -> MonitorParameter a -> MonitorParameter b-(@.) l (MonitorParameter n f) = MonitorParameter n (\x -> f $ x ^. l)+(@.) :: (b -> a) -> MonitorParameter a -> MonitorParameter b+(@.) f (MonitorParameter n m) = MonitorParameter n (m . f)  -- | Monitor 'Int'. monitorInt ::
src/Mcmc/Monitor/ParameterBatch.hs view
@@ -26,7 +26,6 @@  import qualified Data.ByteString.Builder as BB import qualified Data.Double.Conversion.ByteString as BC-import Lens.Micro  -- | Instruction about a parameter to monitor via batch means. Usually, the -- monitored parameter is average over the batch size. However, arbitrary@@ -35,24 +34,22 @@ -- -- XXX: Batch monitors are slow at the moment because the monitored parameter -- has to be extracted from the state for each iteration.-data MonitorParameterBatch a-  = MonitorParameterBatch-      { -- | Name of batch monitored parameter.-        mbpName :: String,-        -- | Instruction about how to extract the batch mean from the trace.-        mbpFunc :: [a] -> BB.Builder-      }+data MonitorParameterBatch a = MonitorParameterBatch+  { -- | Name of batch monitored parameter.+    mbpName :: String,+    -- | Instruction about how to extract the batch mean from the trace.+    mbpFunc :: [a] -> BB.Builder+  } --- | Convert a batch parameter monitor from one data type to another using a--- lens.+-- | Convert a batch parameter monitor from one data type to another. ----- For example, to batch monitor a real float value being the first entry of a tuple:+-- For example, to batch monitor the mean of the first entry of a tuple: -- -- @--- mon = _1 @# monitorBatchMeanRealFloat+-- mon = fst @# monitorBatchMean -- @-(@#) :: Lens' b a -> MonitorParameterBatch a -> MonitorParameterBatch b-(@#) l (MonitorParameterBatch n f) = MonitorParameterBatch n (f . map (^. l))+(@#) :: (b -> a) -> MonitorParameterBatch a -> MonitorParameterBatch b+(@#) f (MonitorParameterBatch n m) = MonitorParameterBatch n (m . map f)  mean :: Real a => [a] -> Double mean xs = realToFrac (sum xs) / fromIntegral (length xs)
src/Mcmc/Monitor/Time.hs view
@@ -19,8 +19,8 @@  import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as BL-import Mcmc.Internal.ByteString import Data.Time.Clock+import Mcmc.Internal.ByteString  -- | Adapted from System.ProgressBar.renderDuration of package -- [terminal-progressbar-0.4.1](https://hackage.haskell.org/package/terminal-progress-bar-0.4.1).
+ src/Mcmc/Prior.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE BangPatterns #-}++-- |+-- Module      :  Prior+-- Description :  Convenience functions to compute priors+-- Copyright   :  (c) Dominik Schrempf, 2020+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  unstable+-- Portability :  portable+--+-- Creation date: Thu Jul 23 13:26:14 2020.+module Mcmc.Prior+  ( -- * Continuous priors+    positive,+    negative,+    uniform,+    normal,+    exponential,+    gamma,++    -- * Discrete priors++    -- No discrete priors are available yet.++    -- * Auxiliary functions+    product',+  )+where++import Control.Monad+import Data.Maybe (fromMaybe)+import Numeric.Log+import qualified Statistics.Distribution as S+import qualified Statistics.Distribution.Exponential as S+import qualified Statistics.Distribution.Gamma as S+import qualified Statistics.Distribution.Normal as S++-- | Improper uniform prior; larger than 0.+positive :: Double -> Log Double+positive x+  | x <= 0 = 0+  | otherwise = 1++-- | Improper uniform prior; lower than 0.+negative :: Double -> Log Double+negative x+  | x >= 0 = 0+  | otherwise = 1++-- | Uniform prior on [a, b].+uniform ::+  -- | Lower bound a.+  Double ->+  -- | Upper bound b.+  Double ->+  Double ->+  Log Double+uniform a b x+  | x <= a = 0+  | x >= b = 0+  | otherwise = Exp 0++-- | Normal distributed prior.+normal ::+  -- | Mean.+  Double ->+  -- | Standard deviation.+  Double ->+  Double ->+  Log Double+normal m s x = Exp $ S.logDensity d x+  where+    d = S.normalDistr m s++-- | Exponential distributed prior.+exponential ::+  -- | Rate.+  Double ->+  Double ->+  Log Double+exponential l x = Exp $ S.logDensity d x+  where+    d = S.exponential l++-- | Gamma distributed prior.+gamma ::+  -- | Shape.+  Double ->+  -- | Scale.+  Double ->+  Double ->+  Log Double+gamma k t x = Exp $ S.logDensity d x+  where+    d = S.gammaDistr k t++-- | Intelligent product that stops when encountering a zero.+--+-- Use with care because the elements have to be checked for positiveness, and+-- this can take some time if the list is long and does not contain any zeroes.+product' :: [Log Double] -> Log Double+product' = fromMaybe 0 . prodM++-- The type could be generalized to any MonadPlus Integer+prodM :: [Log Double] -> Maybe (Log Double)+prodM = foldM (\ !acc x -> (acc * x) <$ guard (acc /= 0)) 1
src/Mcmc/Proposal.hs view
@@ -4,11 +4,6 @@  -- TODO: Proposals on simplices: SimplexElementScale (?). --- TODO: Proposals on tree topologies.--- - NNI--- - Narrow (what is this, see RevBayes)--- - FNPR (dito)- -- | -- Module      :  Mcmc.Proposal -- Description :  Proposals and cycles@@ -34,7 +29,7 @@     Cycle (ccProposals),     fromList,     setOrder,-    getNCycles,+    getNIterations,     tuneCycle,     autotuneCycle,     summarizeCycle,@@ -77,7 +72,7 @@     -- | The weight determines how often a 'Proposal' is executed per iteration of     -- the Markov chain.     pWeight :: Int,-    -- | Simple proposal without tuning information.+    -- | Simple proposal without name, weight, and tuning information.     pSimple :: ProposalSimple a,     -- | Tuning is disabled if set to 'Nothing'.     pTuner :: Maybe (Tuner a)@@ -92,9 +87,6 @@ instance Ord (Proposal a) where   compare = compare `on` pName -convertP :: Lens' b a -> Proposal a -> Proposal b-convertP l (Proposal n w s t) = Proposal n w (convertS l s) (convertT l <$> t)- -- | Convert a proposal from one data type to another using a lens. -- -- For example:@@ -103,7 +95,7 @@ -- scaleFirstEntryOfTuple = scale >>> _1 -- @ (@~) :: Lens' b a -> Proposal a -> Proposal b-(@~) = convertP+(@~) l (Proposal n w s t) = Proposal n w (convertS l s) (convertT l <$> t)  -- | Simple proposal without tuning information. --@@ -167,10 +159,9 @@     let t' = max tuningParamMin (t * dt)     return $ m {pSimple = f t', pTuner = Just $ Tuner t' f} --- XXX: The desired acceptance ratio 0.44 is optimal for one-dimensional--- proposals; one could also store the affected number of dimensions with the--- proposal and tune towards an acceptance ratio accounting for the number of--- dimensions.+-- The desired acceptance ratio 0.44 is optimal for one-dimensional proposals;+-- one could also store the affected number of dimensions with the proposal and+-- tune towards an acceptance ratio accounting for the number of dimensions. -- -- The optimal ratios seem to be: -- - One dimension: 0.44 (numerical result).@@ -180,6 +171,14 @@ ratioOpt :: Double ratioOpt = 0.44 +-- Warn if acceptance ratio is lower.+ratioMin :: Double+ratioMin = 0.1++-- Warn if acceptance ratio is larger.+ratioMax :: Double+ratioMax = 0.9+ -- | Define the order in which 'Proposal's are executed in a 'Cycle'. The total -- number of 'Proposal's per 'Cycle' may differ between 'Order's (e.g., compare -- 'RandomO' and 'RandomReversibleO').@@ -206,11 +205,13 @@  instance Default Order where def = RandomO --- | In brief, a 'Cycle' is a list of proposals. The state of the Markov chain will--- be logged only after all 'Proposal's in the 'Cycle' have been completed, and the--- iteration counter will be increased by one. The order in which the 'Proposal's--- are executed is specified by 'Order'. The default is 'RandomO'.+-- | In brief, a 'Cycle' is a list of proposals. --+-- The state of the Markov chain will be logged only after all 'Proposal's in+-- the 'Cycle' have been completed, and the iteration counter will be increased+-- by one. The order in which the 'Proposal's are executed is specified by+-- 'Order'. The default is 'RandomO'.+-- -- __Proposals must have unique names__, so that they can be identified. data Cycle a = Cycle   { ccProposals :: [Proposal a],@@ -233,8 +234,8 @@ setOrder o c = c {ccOrder = o}  -- | Replicate 'Proposal's according to their weights and possibly shuffle them.-getNCycles :: Cycle a -> Int -> GenIO -> IO [[Proposal a]]-getNCycles (Cycle xs o) n g = case o of+getNIterations :: Cycle a -> Int -> GenIO -> IO [[Proposal a]]+getNIterations (Cycle xs o) n g = case o of   RandomO -> shuffleN ps n g   SequentialO -> return $ replicate n ps   RandomReversibleO -> do@@ -269,8 +270,9 @@   BL.ByteString ->   BL.ByteString ->   BL.ByteString ->+  BL.ByteString ->   BL.ByteString-renderRow name weight nAccept nReject acceptRatio tuneParam = "   " <> nm <> wt <> na <> nr <> ra <> tp+renderRow name weight nAccept nReject acceptRatio tuneParam manualAdjustment = "   " <> nm <> wt <> na <> nr <> ra <> tp <> mt   where     nm = alignLeft 30 name     wt = alignRight 8 weight@@ -278,13 +280,22 @@     nr = alignRight 15 nReject     ra = alignRight 15 acceptRatio     tp = alignRight 20 tuneParam+    mt = alignRight 30 manualAdjustment  proposalHeader :: BL.ByteString proposalHeader =-  renderRow "Proposal" "Weight" "Accepted" "Rejected" "Ratio" "Tuning parameter"+  renderRow "Proposal" "Weight" "Accepted" "Rejected" "Ratio" "Tuning parameter" "Consider manual adjustment"  summarizeProposal :: Proposal a -> Maybe (Int, Int, Double) -> BL.ByteString-summarizeProposal m r = renderRow (BL.pack name) weight nAccept nReject acceptRatio tuneParamStr+summarizeProposal m r =+  renderRow+    (BL.pack name)+    weight+    nAccept+    nReject+    acceptRatio+    tuneParamStr+    manualAdjustmentStr   where     name = pName m     weight = BB.toLazyByteString $ BB.intDec $ pWeight m@@ -292,17 +303,25 @@     nReject = BB.toLazyByteString $ maybe "" (BB.intDec . (^. _2)) r     acceptRatio = BL.fromStrict $ maybe "" (BC.toFixed 3 . (^. _3)) r     tuneParamStr = BL.fromStrict $ maybe "" (BC.toFixed 3) (tParam <$> pTuner m)+    check v+      | v < ratioMin = "ratio too low"+      | v > ratioMax = "ratio too high"+      | otherwise = ""+    manualAdjustmentStr = BL.fromStrict $ maybe "" (check . (^. _3)) r +hLine :: BL.ByteString -> BL.ByteString+hLine s = "   " <> BL.replicate (BL.length s - 3) '-'+ -- | Summarize the 'Proposal's in the 'Cycle'. Also report acceptance ratios. summarizeCycle :: Acceptance (Proposal a) -> Cycle a -> BL.ByteString summarizeCycle a c =   BL.intercalate "\n" $     [ "Summary of proposal(s) in cycle. " <> mpi <> " proposal(s) per iteration.",       proposalHeader,-      "   " <> BL.replicate (BL.length proposalHeader - 3) '-'+      hLine proposalHeader     ]       ++ [summarizeProposal m (ar m) | m <- ps]-      ++ ["   " <> BL.replicate (BL.length proposalHeader - 3) '-']+      ++ [hLine proposalHeader]   where     ps = ccProposals c     mpi = BB.toLazyByteString $ BB.intDec $ sum $ map pWeight ps
src/Mcmc/Proposal/Bactrian.hs view
@@ -99,7 +99,7 @@ -- Hence, -- dx' = 1/(1-dx) - 1. fInv :: Double -> Double-fInv dx = recip (1-dx) - 1+fInv dx = recip (1 - dx) - 1  bactrianMult ::   Double ->
src/Mcmc/Proposal/Generic.hs view
@@ -1,8 +1,3 @@--- Technically, only a Getter is needed when calculating the kernel of the proposal--- ('kernelCont', and similar functions). I tried splitting the lens into a getter--- and a setter. However, speed improvements were marginal, and some times not--- even measurable. Using a 'Lens'' is just easier, and has no real drawbacks.- -- | -- Module      :  Mcmc.Proposal.Generic -- Description :  Generic interface to create proposals@@ -15,8 +10,8 @@ -- -- Creation date: Thu May 14 20:26:27 2020. module Mcmc.Proposal.Generic-  ( proposalGenericContinuous,-    proposalGenericDiscrete,+  ( genericContinuous,+    genericDiscrete,   ) where @@ -45,7 +40,7 @@ {-# INLINEABLE sampleCont #-}  -- | Generic function to create proposals for continuous parameters ('Double').-proposalGenericContinuous ::+genericContinuous ::   (ContDistr d, ContGen d) =>   -- | Probability distribution   d ->@@ -55,7 +50,7 @@   -- required for biased proposals.   Maybe (Double -> Double) ->   ProposalSimple a-proposalGenericContinuous d f fInv = ProposalSimple $ sampleCont d f fInv+genericContinuous d f fInv = ProposalSimple $ sampleCont d f fInv  sampleDiscrete ::   (DiscreteDistr d, DiscreteGen d) =>@@ -77,7 +72,7 @@ {-# INLINEABLE sampleDiscrete #-}  -- | Generic function to create proposals for discrete parameters ('Int').-proposalGenericDiscrete ::+genericDiscrete ::   (DiscreteDistr d, DiscreteGen d) =>   -- | Probability distribution.   d ->@@ -87,4 +82,4 @@   -- required for biased proposals.   Maybe (Int -> Int) ->   ProposalSimple a-proposalGenericDiscrete fd f fInv = ProposalSimple $ sampleDiscrete fd f fInv+genericDiscrete fd f fInv = ProposalSimple $ sampleDiscrete fd f fInv
src/Mcmc/Proposal/Scale.hs view
@@ -14,6 +14,7 @@ module Mcmc.Proposal.Scale   ( scale,     scaleUnbiased,+    scaleContrarily,   ) where @@ -24,7 +25,7 @@ -- The actual proposal with tuning parameter. The tuning parameter does not -- change the mean. scaleSimple :: Double -> Double -> Double -> ProposalSimple Double-scaleSimple k th t = proposalGenericContinuous (gammaDistr (k / t) (th * t)) (*) (Just recip)+scaleSimple k th t = genericContinuous (gammaDistr (k / t) (th * t)) (*) (Just recip)  -- | Multiplicative proposal with Gamma distributed kernel. scale ::@@ -41,9 +42,10 @@   Proposal Double scale n w k th = createProposal n w (scaleSimple k th) --- | Multiplicative proposal with Gamma distributed kernel. The scale of the Gamma--- distributions is set to (shape)^{-1}, so that the mean of the Gamma--- distribution is 1.0.+-- | Multiplicative proposal with Gamma distributed kernel.+--+-- The scale of the Gamma distributions is set to (shape)^{-1}, so that the mean+-- of the Gamma distribution is 1.0. scaleUnbiased ::   -- | Name.   String ->@@ -55,3 +57,27 @@   Bool ->   Proposal Double scaleUnbiased n w k = createProposal n w (scaleSimple k (1 / k))++contra :: (Double, Double) -> Double -> (Double, Double)+contra (x, y) z = (x*z, y/z)++scaleContrarilySimple :: Double -> Double -> Double -> ProposalSimple (Double, Double)+scaleContrarilySimple k th t = genericContinuous (gammaDistr (k / t) (th * t)) contra (Just recip)++-- | Multiplicative proposal with Gamma distributed kernel.+--+-- The two values are scaled contrarily so that their product stays constant.+-- Contrary proposals are useful when parameters are confounded.+scaleContrarily ::+  -- | Name.+  String ->+  -- | Weight.+  Int ->+  -- | Shape.+  Double ->+  -- | Scale.+  Double ->+  -- | Enable tuning.+  Bool ->+  Proposal (Double, Double)+scaleContrarily n w k th = createProposal n w (scaleContrarilySimple k th)
src/Mcmc/Proposal/Slide.hs view
@@ -15,6 +15,7 @@   ( slide,     slideSymmetric,     slideUniform,+    slideContrarily,   ) where @@ -25,7 +26,7 @@  -- The actual proposal with tuning parameter. slideSimple :: Double -> Double -> Double -> ProposalSimple Double-slideSimple m s t = proposalGenericContinuous (normalDistr m (s * t)) (+) (Just negate)+slideSimple m s t = genericContinuous (normalDistr m (s * t)) (+) (Just negate)  -- | Additive proposal with normally distributed kernel. slide ::@@ -44,7 +45,7 @@  -- The actual proposal with tuning parameter. slideSymmetricSimple :: Double -> Double -> ProposalSimple Double-slideSymmetricSimple s t = proposalGenericContinuous (normalDistr 0.0 (s * t)) (+) Nothing+slideSymmetricSimple s t = genericContinuous (normalDistr 0.0 (s * t)) (+) Nothing  -- | Additive proposal with normally distributed kernel with mean zero. This -- proposal is very fast, because the Metropolis-Hastings ratio does not include@@ -64,7 +65,7 @@ -- The actual proposal with tuning parameter. slideUniformSimple :: Double -> Double -> ProposalSimple Double slideUniformSimple d t =-  proposalGenericContinuous (uniformDistr (- t * d) (t * d)) (+) Nothing+  genericContinuous (uniformDistr (- t * d) (t * d)) (+) Nothing  -- | Additive proposal with uniformly distributed kernel. This proposal is very fast, -- because the Metropolis-Hastings ratio does not include calculation of the@@ -80,3 +81,27 @@   Bool ->   Proposal Double slideUniform n w d = createProposal n w (slideUniformSimple d)++contra :: (Double, Double) -> Double -> (Double, Double)+contra (x, y) d = (x + d, y - d)++slideContrarilySimple :: Double -> Double -> Double -> ProposalSimple (Double, Double)+slideContrarilySimple m s t = genericContinuous (normalDistr m (s * t)) contra (Just negate)++-- | Additive proposal with normally distributed kernel.+--+-- The two values are slid contrarily so that their sum stays constant. Contrary+-- proposals are useful when parameters are confounded.+slideContrarily ::+  -- | Name.+  String ->+  -- | Weight.+  Int ->+  -- | Mean.+  Double ->+  -- | Standard deviation.+  Double ->+  -- | Enable tuning.+  Bool ->+  Proposal (Double, Double)+slideContrarily n w m s = createProposal n w (slideContrarilySimple m s)
src/Mcmc/Save.hs view
@@ -26,10 +26,10 @@ import Control.Monad import Data.Aeson import Data.Aeson.TH-import Data.Maybe import qualified Data.ByteString.Lazy.Char8 as BL import Data.List hiding (cycle) import qualified Data.Map as M+import Data.Maybe import Data.Vector.Unboxed (Vector) import Data.Word -- TODO: Splitmix. Reproposal as soon as split mix is used and is available with the@@ -153,7 +153,7 @@ -- - cycle -- - monitor ----- To avoid incomplete continued runs, the 'mcmc' file is removed after load.+-- To avoid incomplete continued runs, the @.mcmc@ file is removed after load. loadStatus ::   FromJSON a =>   (a -> Log Double) ->
src/Mcmc/Tools/Shuffle.hs view
@@ -20,8 +20,8 @@  import Control.Monad import Control.Monad.ST-import qualified Data.Vector as V import Data.Vector (Vector)+import qualified Data.Vector as V import qualified Data.Vector.Mutable as M import System.Random.MWC   ( GenIO,@@ -49,9 +49,10 @@ -- elements from @xs@, without replacement, and that @m@ times. grabble :: [a] -> Int -> Int -> GenIO -> IO [[a]] grabble xs m n gen = do-  swapss <- replicateM m $ forM [0 .. min (l - 1) n] $ \i -> do-    j <- uniformR (i, l) gen-    return (i, j)+  swapss <- replicateM m $+    forM [0 .. min (l - 1) n] $ \i -> do+      j <- uniformR (i, l) gen+      return (i, j)   return $ map (V.toList . V.take n . swapElems (V.fromList xs)) swapss   where     l = length xs - 1
test/Mcmc/ProposalSpec.hs view
@@ -30,14 +30,14 @@  spec :: Spec spec =-  describe "getNCycles"-    $ it "returns the correct number of proposals in a cycle"-    $ do-      g <- create-      l1 <- length . head <$> getNCycles c 1 g-      l1 `shouldBe` 4-      l2 <- length . head <$> getNCycles (setOrder RandomReversibleO c) 1 g-      l2 `shouldBe` 8-      o3 <- head <$> getNCycles (setOrder SequentialReversibleO c) 1 g-      length o3 `shouldBe` 8-      o3 `shouldBe` [p1, p2, p2, p2, p2, p2, p2, p1]+  describe "getNIterations" $+    it "returns the correct number of proposals in a cycle" $+      do+        g <- create+        l1 <- length . head <$> getNIterations c 1 g+        l1 `shouldBe` 4+        l2 <- length . head <$> getNIterations (setOrder RandomReversibleO c) 1 g+        l2 `shouldBe` 8+        o3 <- head <$> getNIterations (setOrder SequentialReversibleO c) 1 g+        length o3 `shouldBe` 8+        o3 `shouldBe` [p1, p2, p2, p2, p2, p2, p2, p1]
test/Mcmc/SaveSpec.hs view
@@ -61,27 +61,30 @@  spec :: Spec spec =-  describe "saveStatus and loadStatus"-    $ it "doesn't change the MCMC chain"-    $ do-      gen <- create-      let s =-            force $ quiet $ saveWith 100 $-              status "SaveSpec" (const 1) lh proposals mon 0 nBurn nAutoTune nIter gen-      saveStatus "SaveSpec.json" s-      s' <- loadStatus (const 1) lh proposals mon "SaveSpec.json"-      r <- mh s-      r' <- mh s'-      -- Done during 'loadStatus'.-      -- removeFile "SaveSpec.json"-      item r `shouldBe` item r'-      iteration r `shouldBe` iteration r'-      trace r `shouldBe` trace r'-      g <- save $ generator r-      g' <- save $ generator r'-      g `shouldBe` g'+  describe "saveStatus and loadStatus" $+    it "doesn't change the MCMC chain" $+      do+        gen <- create+        let s =+              force $+                quiet $+                  saveWith 100 $+                    status "SaveSpec" (const 1) lh proposals mon 0 nBurn nAutoTune nIter gen+        saveStatus "SaveSpec.json" s+        s' <- loadStatus (const 1) lh proposals mon "SaveSpec.json"+        r <- mh s+        r' <- mh s'+        -- Done during 'loadStatus'.+        -- removeFile "SaveSpec.json"+        item r `shouldBe` item r'+        iteration r `shouldBe` iteration r'+        trace r `shouldBe` trace r'+        g <- save $ generator r+        g' <- save $ generator r'+        g `shouldBe` g'+ -- -- TODO: Splitmix. This will only work with a splittable generator--- -- because getNCycles changes the generator.+-- -- because getNIterations changes the generator. -- describe "mhContinue" --   $ it "mh 200 + mhContinue 200 == mh 400" --   $ do