mcmc 0.4.0.0 → 0.5.0.0
raw patch · 39 files changed
+1808/−849 lines, 39 filesdep ~aesondep ~bytestringdep ~circularPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, bytestring, circular, containers, criterion, data-default, deepseq, directory, dirichlet, double-conversion, hspec, log-domain, microlens, monad-parallel, mwc-random, pretty-show, primitive, statistics, time, transformers, vector, zlib
API changes (from Hackage documentation)
- Mcmc: PWeight :: Int -> PWeight
- Mcmc: [fromPWeight] :: PWeight -> Int
- Mcmc: newtype PWeight
- Mcmc: noLikelihood :: LikelihoodFunction a
- Mcmc: noPrior :: PriorFunction a
- Mcmc: type LikelihoodFunction a = a -> Log Double
- Mcmc: type PriorFunction a = a -> Log Double
- Mcmc.Algorithm.Metropolis: MHG :: Chain a -> MHG a
- Mcmc.Algorithm.Metropolis: [fromMHG] :: MHG a -> Chain a
- Mcmc.Algorithm.Metropolis: instance Data.Aeson.Types.ToJSON.ToJSON a => Mcmc.Algorithm.Algorithm (Mcmc.Algorithm.Metropolis.MHG a)
- Mcmc.Algorithm.Metropolis: mhg :: PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> a -> GenIO -> IO (MHG a)
- Mcmc.Algorithm.Metropolis: mhgAccept :: Log Double -> GenIO -> IO Bool
- Mcmc.Algorithm.Metropolis: mhgLoad :: FromJSON a => PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> AnalysisName -> IO (MHG a)
- Mcmc.Algorithm.Metropolis: mhgSave :: ToJSON a => AnalysisName -> MHG a -> IO ()
- Mcmc.Algorithm.Metropolis: newtype MHG a
- Mcmc.Environment: [logHandle] :: Environment -> Maybe Handle
- Mcmc.Environment: instance GHC.Classes.Eq Mcmc.Environment.Environment
- Mcmc.Environment: instance GHC.Show.Show Mcmc.Environment.Environment
- Mcmc.Proposal: PWeight :: Int -> PWeight
- Mcmc.Proposal: [fromPWeight] :: PWeight -> Int
- Mcmc.Proposal: [pDescription] :: Proposal a -> PDescription
- Mcmc.Proposal: [pDimension] :: Proposal a -> PDimension
- Mcmc.Proposal: [pName] :: Proposal a -> PName
- Mcmc.Proposal: [pSimple] :: Proposal a -> ProposalSimple a
- Mcmc.Proposal: [pTuner] :: Proposal a -> Maybe (Tuner a)
- Mcmc.Proposal: [pWeight] :: Proposal a -> PWeight
- Mcmc.Proposal: newtype PWeight
- Mcmc.Proposal: orderProposals :: Cycle a -> GenIO -> IO [Proposal a]
- Mcmc.Settings: instance Data.Aeson.Types.FromJSON.FromJSON Mcmc.Settings.Verbosity
- Mcmc.Settings: instance Data.Aeson.Types.ToJSON.ToJSON Mcmc.Settings.Verbosity
- Mcmc.Settings: instance GHC.Classes.Eq Mcmc.Settings.Verbosity
- Mcmc.Settings: instance GHC.Classes.Ord Mcmc.Settings.Verbosity
- Mcmc.Settings: instance GHC.Read.Read Mcmc.Settings.Verbosity
- Mcmc.Settings: instance GHC.Show.Show Mcmc.Settings.Verbosity
+ Mcmc: Exp :: a -> Log a
+ Mcmc: [ln] :: Log a -> a
+ Mcmc: data PWeight
+ Mcmc: infixl 7 @~
+ Mcmc: liftProposal :: Lens' b a -> Proposal a -> Proposal b
+ Mcmc: liftProposalWith :: JacobianFunction b -> Lens' b a -> Proposal a -> Proposal b
+ Mcmc: newtype Log a
+ Mcmc: pWeight :: Int -> PWeight
+ Mcmc: simpleMonitor :: Period -> Monitor a
+ Mcmc: type JacobianFunction a = a -> Jacobian
+ Mcmc.Algorithm: aIsInValidState :: Algorithm a => a -> Bool
+ Mcmc.Algorithm.MHG: MHG :: Chain a -> MHG a
+ Mcmc.Algorithm.MHG: [fromMHG] :: MHG a -> Chain a
+ Mcmc.Algorithm.MHG: instance Data.Aeson.Types.ToJSON.ToJSON a => Mcmc.Algorithm.Algorithm (Mcmc.Algorithm.MHG.MHG a)
+ Mcmc.Algorithm.MHG: mhg :: PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> TraceLength -> InitialState a -> GenIO -> IO (MHG a)
+ Mcmc.Algorithm.MHG: mhgAccept :: MHGRatio -> GenIO -> IO Bool
+ Mcmc.Algorithm.MHG: mhgLoad :: FromJSON a => PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> AnalysisName -> IO (MHG a)
+ Mcmc.Algorithm.MHG: mhgSave :: ToJSON a => AnalysisName -> MHG a -> IO ()
+ Mcmc.Algorithm.MHG: newtype MHG a
+ Mcmc.Algorithm.MHG: type MHGRatio = Log Double
+ Mcmc.Chain.Chain: type InitialState a = a
+ Mcmc.Environment: [logHandles] :: Environment s -> [Handle]
+ Mcmc.Environment: [outLock] :: Environment s -> MVar ()
+ Mcmc.Environment: closeEnvironment :: Environment s -> IO ()
+ Mcmc.Environment: instance GHC.Classes.Eq s => GHC.Classes.Eq (Mcmc.Environment.Environment s)
+ Mcmc.Environment: instance Mcmc.Logger.HasLock (Mcmc.Environment.Environment s)
+ Mcmc.Environment: instance Mcmc.Logger.HasLogHandles (Mcmc.Environment.Environment s)
+ Mcmc.Environment: instance Mcmc.Logger.HasLogMode s => Mcmc.Logger.HasLogMode (Mcmc.Environment.Environment s)
+ Mcmc.Environment: instance Mcmc.Logger.HasStartingTime (Mcmc.Environment.Environment s)
+ Mcmc.Environment: instance Mcmc.Logger.HasVerbosity s => Mcmc.Logger.HasVerbosity (Mcmc.Environment.Environment s)
+ Mcmc.Environment: instance Mcmc.Settings.HasExecutionMode s => Mcmc.Settings.HasExecutionMode (Mcmc.Environment.Environment s)
+ Mcmc.Likelihood: noLikelihood :: LikelihoodFunction a
+ Mcmc.Likelihood: type Likelihood = Log Double
+ Mcmc.Likelihood: type LikelihoodFunction a = a -> Log Double
+ Mcmc.Logger: Debug :: Verbosity
+ Mcmc.Logger: Info :: Verbosity
+ Mcmc.Logger: LogFileOnly :: LogMode
+ Mcmc.Logger: LogStdOutAndFile :: LogMode
+ Mcmc.Logger: LogStdOutOnly :: LogMode
+ Mcmc.Logger: Quiet :: Verbosity
+ Mcmc.Logger: Warn :: Verbosity
+ Mcmc.Logger: class HasLock e
+ Mcmc.Logger: class HasLogHandles e
+ Mcmc.Logger: class HasLogMode s
+ Mcmc.Logger: class HasStartingTime s
+ Mcmc.Logger: class HasVerbosity s
+ Mcmc.Logger: data LogMode
+ Mcmc.Logger: data Verbosity
+ Mcmc.Logger: getLock :: HasLock e => e -> MVar ()
+ Mcmc.Logger: getLogHandles :: HasLogHandles e => e -> [Handle]
+ Mcmc.Logger: getLogMode :: HasLogMode s => s -> LogMode
+ Mcmc.Logger: getStartingTime :: HasStartingTime s => s -> UTCTime
+ Mcmc.Logger: getVerbosity :: HasVerbosity s => s -> Verbosity
+ Mcmc.Logger: instance Data.Aeson.Types.FromJSON.FromJSON Mcmc.Logger.LogMode
+ Mcmc.Logger: instance Data.Aeson.Types.FromJSON.FromJSON Mcmc.Logger.Verbosity
+ Mcmc.Logger: instance Data.Aeson.Types.ToJSON.ToJSON Mcmc.Logger.LogMode
+ Mcmc.Logger: instance Data.Aeson.Types.ToJSON.ToJSON Mcmc.Logger.Verbosity
+ Mcmc.Logger: instance GHC.Classes.Eq Mcmc.Logger.LogMode
+ Mcmc.Logger: instance GHC.Classes.Eq Mcmc.Logger.Verbosity
+ Mcmc.Logger: instance GHC.Classes.Ord Mcmc.Logger.Verbosity
+ Mcmc.Logger: instance GHC.Read.Read Mcmc.Logger.LogMode
+ Mcmc.Logger: instance GHC.Read.Read Mcmc.Logger.Verbosity
+ Mcmc.Logger: instance GHC.Show.Show Mcmc.Logger.LogMode
+ Mcmc.Logger: instance GHC.Show.Show Mcmc.Logger.Verbosity
+ Mcmc.Logger: logDebugB :: (HasLock e, HasLogHandles e, HasVerbosity e) => ByteString -> Logger e ()
+ Mcmc.Logger: logDebugS :: (HasLock e, HasLogHandles e, HasVerbosity e) => String -> Logger e ()
+ Mcmc.Logger: logInfoB :: (HasLock e, HasLogHandles e, HasVerbosity e) => ByteString -> Logger e ()
+ Mcmc.Logger: logInfoEndTime :: (HasLock e, HasLogHandles e, HasStartingTime e, HasVerbosity e) => Logger e ()
+ Mcmc.Logger: logInfoHeader :: (HasLock e, HasLogHandles e, HasVerbosity e) => Logger e ()
+ Mcmc.Logger: logInfoS :: (HasLock e, HasLogHandles e, HasVerbosity e) => String -> Logger e ()
+ Mcmc.Logger: logInfoStartingTime :: (HasLock e, HasLogHandles e, HasStartingTime e, HasVerbosity e) => Logger e ()
+ Mcmc.Logger: logOutB :: (HasLogHandles e, HasLock e) => ByteString -> ByteString -> Logger e ()
+ Mcmc.Logger: logWarnB :: (HasLock e, HasLogHandles e, HasVerbosity e) => ByteString -> Logger e ()
+ Mcmc.Logger: logWarnS :: (HasLock e, HasLogHandles e, HasVerbosity e) => String -> Logger e ()
+ Mcmc.Logger: type Logger e a = ReaderT e IO a
+ Mcmc.MarginalLikelihood: MLSettings :: AnalysisName -> MLAlgorithm -> NPoints -> BurnInSpecification -> BurnInSpecification -> Iterations -> ExecutionMode -> LogMode -> Verbosity -> MLSettings
+ Mcmc.MarginalLikelihood: NPoints :: Int -> NPoints
+ Mcmc.MarginalLikelihood: SteppingStoneSampling :: MLAlgorithm
+ Mcmc.MarginalLikelihood: ThermodynamicIntegration :: MLAlgorithm
+ Mcmc.MarginalLikelihood: [fromNPoints] :: NPoints -> Int
+ Mcmc.MarginalLikelihood: [mlAlgorithm] :: MLSettings -> MLAlgorithm
+ Mcmc.MarginalLikelihood: [mlAnalysisName] :: MLSettings -> AnalysisName
+ Mcmc.MarginalLikelihood: [mlExecutionMode] :: MLSettings -> ExecutionMode
+ Mcmc.MarginalLikelihood: [mlInitialBurnIn] :: MLSettings -> BurnInSpecification
+ Mcmc.MarginalLikelihood: [mlIterations] :: MLSettings -> Iterations
+ Mcmc.MarginalLikelihood: [mlLogMode] :: MLSettings -> LogMode
+ Mcmc.MarginalLikelihood: [mlNPoints] :: MLSettings -> NPoints
+ Mcmc.MarginalLikelihood: [mlPointBurnIn] :: MLSettings -> BurnInSpecification
+ Mcmc.MarginalLikelihood: [mlVerbosity] :: MLSettings -> Verbosity
+ Mcmc.MarginalLikelihood: data MLAlgorithm
+ Mcmc.MarginalLikelihood: data MLSettings
+ Mcmc.MarginalLikelihood: instance GHC.Classes.Eq Mcmc.MarginalLikelihood.MLAlgorithm
+ Mcmc.MarginalLikelihood: instance GHC.Classes.Eq Mcmc.MarginalLikelihood.MLSettings
+ Mcmc.MarginalLikelihood: instance GHC.Classes.Eq Mcmc.MarginalLikelihood.NPoints
+ Mcmc.MarginalLikelihood: instance GHC.Read.Read Mcmc.MarginalLikelihood.MLAlgorithm
+ Mcmc.MarginalLikelihood: instance GHC.Read.Read Mcmc.MarginalLikelihood.MLSettings
+ Mcmc.MarginalLikelihood: instance GHC.Read.Read Mcmc.MarginalLikelihood.NPoints
+ Mcmc.MarginalLikelihood: instance GHC.Show.Show Mcmc.MarginalLikelihood.MLAlgorithm
+ Mcmc.MarginalLikelihood: instance GHC.Show.Show Mcmc.MarginalLikelihood.MLSettings
+ Mcmc.MarginalLikelihood: instance GHC.Show.Show Mcmc.MarginalLikelihood.NPoints
+ Mcmc.MarginalLikelihood: instance Mcmc.Logger.HasLogMode Mcmc.MarginalLikelihood.MLSettings
+ Mcmc.MarginalLikelihood: instance Mcmc.Logger.HasVerbosity Mcmc.MarginalLikelihood.MLSettings
+ Mcmc.MarginalLikelihood: instance Mcmc.Settings.HasAnalysisName Mcmc.MarginalLikelihood.MLSettings
+ Mcmc.MarginalLikelihood: instance Mcmc.Settings.HasExecutionMode Mcmc.MarginalLikelihood.MLSettings
+ Mcmc.MarginalLikelihood: marginalLikelihood :: ToJSON a => MLSettings -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> InitialState a -> GenIO -> IO MarginalLikelihood
+ Mcmc.MarginalLikelihood: newtype NPoints
+ Mcmc.MarginalLikelihood: type MarginalLikelihood = Log Double
+ Mcmc.Monitor: simpleMonitor :: Period -> Monitor a
+ Mcmc.Monitor: type BatchSize = Int
+ Mcmc.Monitor: type Period = Int
+ Mcmc.Posterior: type Posterior = Log Double
+ Mcmc.Prior: gammaMeanOne :: Shape -> PriorFunction Double
+ Mcmc.Prior: gammaMeanVariance :: Mean -> Variance -> PriorFunction Double
+ Mcmc.Prior: gammaMeanVarianceToShapeScale :: Mean -> Variance -> (Shape, Scale)
+ Mcmc.Prior: gammaShapeScaleToMeanVariance :: Shape -> Scale -> (Mean, Variance)
+ Mcmc.Prior: noPrior :: PriorFunction a
+ Mcmc.Prior: type Prior = Log Double
+ Mcmc.Prior: type PriorFunction a = a -> Prior
+ Mcmc.Proposal: Tuner :: TuningParameter -> (TuningParameter -> ProposalSimple a) -> Tuner a
+ Mcmc.Proposal: [prDescription] :: Proposal a -> PDescription
+ Mcmc.Proposal: [prDimension] :: Proposal a -> PDimension
+ Mcmc.Proposal: [prName] :: Proposal a -> PName
+ Mcmc.Proposal: [prSimple] :: Proposal a -> ProposalSimple a
+ Mcmc.Proposal: [prTuner] :: Proposal a -> Maybe (Tuner a)
+ Mcmc.Proposal: [prWeight] :: Proposal a -> PWeight
+ Mcmc.Proposal: [tFunc] :: Tuner a -> TuningParameter -> ProposalSimple a
+ Mcmc.Proposal: [tParam] :: Tuner a -> TuningParameter
+ Mcmc.Proposal: data PWeight
+ Mcmc.Proposal: infixl 7 @~
+ Mcmc.Proposal: liftProposal :: Lens' b a -> Proposal a -> Proposal b
+ Mcmc.Proposal: liftProposalWith :: JacobianFunction b -> Lens' b a -> Proposal a -> Proposal b
+ Mcmc.Proposal: pWeight :: Int -> PWeight
+ Mcmc.Proposal: prepareProposals :: Cycle a -> GenIO -> IO [Proposal a]
+ Mcmc.Proposal: tuningParameterMax :: TuningParameter
+ Mcmc.Proposal: tuningParameterMin :: TuningParameter
+ Mcmc.Proposal: type Jacobian = Log Double
+ Mcmc.Proposal: type JacobianFunction a = a -> Jacobian
+ Mcmc.Proposal: type KernelRatio = Log Double
+ Mcmc.Proposal: type TuningParameter = Double
+ Mcmc.Proposal.Bactrian: type SpikeParameter = Double
+ Mcmc.Settings: BurnInWithCustomAutoTuning :: [Int] -> BurnInSpecification
+ Mcmc.Settings: LogFileOnly :: LogMode
+ Mcmc.Settings: LogStdOutAndFile :: LogMode
+ Mcmc.Settings: LogStdOutOnly :: LogMode
+ Mcmc.Settings: TraceAuto :: TraceLength
+ Mcmc.Settings: TraceMinimum :: Int -> TraceLength
+ Mcmc.Settings: [sLogMode] :: Settings -> LogMode
+ Mcmc.Settings: class HasAnalysisName s
+ Mcmc.Settings: class HasExecutionMode s
+ Mcmc.Settings: data LogMode
+ Mcmc.Settings: data TraceLength
+ Mcmc.Settings: getAnalysisName :: HasAnalysisName s => s -> AnalysisName
+ Mcmc.Settings: getExecutionMode :: HasExecutionMode s => s -> ExecutionMode
+ Mcmc.Settings: instance GHC.Base.Monoid Mcmc.Settings.AnalysisName
+ Mcmc.Settings: instance GHC.Base.Semigroup Mcmc.Settings.AnalysisName
+ Mcmc.Settings: instance Mcmc.Logger.HasLogMode Mcmc.Settings.Settings
+ Mcmc.Settings: instance Mcmc.Logger.HasVerbosity Mcmc.Settings.Settings
+ Mcmc.Settings: instance Mcmc.Settings.HasAnalysisName Mcmc.Settings.Settings
+ Mcmc.Settings: instance Mcmc.Settings.HasExecutionMode Mcmc.Settings.Settings
+ Mcmc.Statistics.Types: type Dimension = Int
+ Mcmc.Statistics.Types: type LowerBoundary = Double
+ Mcmc.Statistics.Types: type Mean = Double
+ Mcmc.Statistics.Types: type Rate = Double
+ Mcmc.Statistics.Types: type Scale = Double
+ Mcmc.Statistics.Types: type Shape = Double
+ Mcmc.Statistics.Types: type Size = Double
+ Mcmc.Statistics.Types: type StandardDeviation = Double
+ Mcmc.Statistics.Types: type UpperBoundary = Double
+ Mcmc.Statistics.Types: type Variance = Double
- Mcmc: mcmcContinue :: Algorithm a => Int -> Settings -> a -> IO a
+ Mcmc: mcmcContinue :: Algorithm a => Iterations -> Settings -> a -> IO a
- Mcmc: monitorBatch :: String -> [MonitorParameterBatch a] -> Int -> MonitorBatch a
+ Mcmc: monitorBatch :: String -> [MonitorParameterBatch a] -> BatchSize -> MonitorBatch a
- Mcmc: monitorFile :: String -> [MonitorParameter a] -> Int -> MonitorFile a
+ Mcmc: monitorFile :: String -> [MonitorParameter a] -> Period -> MonitorFile a
- Mcmc: monitorStdOut :: [MonitorParameter a] -> Int -> MonitorStdOut a
+ Mcmc: monitorStdOut :: [MonitorParameter a] -> Period -> MonitorStdOut a
- Mcmc: scale :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: scale :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: scaleBactrian :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: scaleBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: scaleContrarily :: Double -> Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc: scaleContrarily :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc: scaleUnbiased :: Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: scaleUnbiased :: Shape -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: slide :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: slide :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: slideBactrian :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: slideBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: slideContrarily :: Double -> Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc: slideContrarily :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc: slideSymmetric :: Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: slideSymmetric :: StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: slideUniformSymmetric :: Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: slideUniformSymmetric :: Size -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Algorithm.MC3: mc3 :: MC3Settings -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> a -> GenIO -> IO (MC3 a)
+ Mcmc.Algorithm.MC3: mc3 :: MC3Settings -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> TraceLength -> InitialState a -> GenIO -> IO (MC3 a)
- Mcmc.Chain.Chain: Chain :: Int -> Link a -> Int -> Trace a -> Acceptance (Proposal a) -> GenIO -> Int -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> Chain a
+ Mcmc.Chain.Chain: Chain :: Maybe Int -> Link a -> Int -> Trace a -> Acceptance (Proposal a) -> GenIO -> Int -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> Chain a
- Mcmc.Chain.Chain: [chainId] :: Chain a -> Int
+ Mcmc.Chain.Chain: [chainId] :: Chain a -> Maybe Int
- Mcmc.Chain.Chain: type PriorFunction a = a -> Log Double
+ Mcmc.Chain.Chain: type PriorFunction a = a -> Prior
- Mcmc.Chain.Link: Link :: a -> Log Double -> Log Double -> Link a
+ Mcmc.Chain.Link: Link :: a -> Prior -> Likelihood -> Link a
- Mcmc.Chain.Link: [likelihood] :: Link a -> Log Double
+ Mcmc.Chain.Link: [likelihood] :: Link a -> Likelihood
- Mcmc.Chain.Link: [prior] :: Link a -> Log Double
+ Mcmc.Chain.Link: [prior] :: Link a -> Prior
- Mcmc.Chain.Save: SavedChain :: Int -> Link a -> Int -> Stack Vector (Link a) -> Acceptance Int -> Vector Word32 -> [Maybe Double] -> SavedChain a
+ Mcmc.Chain.Save: SavedChain :: Maybe Int -> Link a -> Int -> Stack Vector (Link a) -> Acceptance Int -> Vector Word32 -> [Maybe TuningParameter] -> SavedChain a
- Mcmc.Chain.Save: [savedId] :: SavedChain a -> Int
+ Mcmc.Chain.Save: [savedId] :: SavedChain a -> Maybe Int
- Mcmc.Chain.Save: [savedTuningParameters] :: SavedChain a -> [Maybe Double]
+ Mcmc.Chain.Save: [savedTuningParameters] :: SavedChain a -> [Maybe TuningParameter]
- Mcmc.Environment: Environment :: Settings -> Maybe Handle -> UTCTime -> Environment
+ Mcmc.Environment: Environment :: s -> [Handle] -> MVar () -> UTCTime -> Environment s
- Mcmc.Environment: [settings] :: Environment -> Settings
+ Mcmc.Environment: [settings] :: Environment s -> s
- Mcmc.Environment: [startingTime] :: Environment -> UTCTime
+ Mcmc.Environment: [startingTime] :: Environment s -> UTCTime
- Mcmc.Environment: data Environment
+ Mcmc.Environment: data Environment s
- Mcmc.Environment: initializeEnvironment :: Settings -> IO Environment
+ Mcmc.Environment: initializeEnvironment :: (HasAnalysisName s, HasExecutionMode s, HasLogMode s, HasVerbosity s) => s -> IO (Environment s)
- Mcmc.Mcmc: mcmcContinue :: Algorithm a => Int -> Settings -> a -> IO a
+ Mcmc.Mcmc: mcmcContinue :: Algorithm a => Iterations -> Settings -> a -> IO a
- Mcmc.Monitor: getMonitorBatchSize :: MonitorBatch a -> Int
+ Mcmc.Monitor: getMonitorBatchSize :: MonitorBatch a -> BatchSize
- Mcmc.Monitor: monitorBatch :: String -> [MonitorParameterBatch a] -> Int -> MonitorBatch a
+ Mcmc.Monitor: monitorBatch :: String -> [MonitorParameterBatch a] -> BatchSize -> MonitorBatch a
- Mcmc.Monitor: monitorFile :: String -> [MonitorParameter a] -> Int -> MonitorFile a
+ Mcmc.Monitor: monitorFile :: String -> [MonitorParameter a] -> Period -> MonitorFile a
- Mcmc.Monitor: monitorStdOut :: [MonitorParameter a] -> Int -> MonitorStdOut a
+ Mcmc.Monitor: monitorStdOut :: [MonitorParameter a] -> Period -> MonitorStdOut a
- Mcmc.Prior: exponential :: Double -> Double -> Log Double
+ Mcmc.Prior: exponential :: Rate -> PriorFunction Double
- Mcmc.Prior: gamma :: Double -> Double -> Double -> Log Double
+ Mcmc.Prior: gamma :: Shape -> Scale -> PriorFunction Double
- Mcmc.Prior: largerThan :: Double -> Double -> Log Double
+ Mcmc.Prior: largerThan :: LowerBoundary -> PriorFunction Double
- Mcmc.Prior: lowerThan :: Double -> Double -> Log Double
+ Mcmc.Prior: lowerThan :: UpperBoundary -> PriorFunction Double
- Mcmc.Prior: negative :: Double -> Log Double
+ Mcmc.Prior: negative :: PriorFunction Double
- Mcmc.Prior: normal :: Double -> Double -> Double -> Log Double
+ Mcmc.Prior: normal :: Mean -> StandardDeviation -> PriorFunction Double
- Mcmc.Prior: poisson :: Double -> Int -> Log Double
+ Mcmc.Prior: poisson :: Rate -> PriorFunction Int
- Mcmc.Prior: positive :: Double -> Log Double
+ Mcmc.Prior: positive :: PriorFunction Double
- Mcmc.Prior: uniform :: Double -> Double -> Double -> Log Double
+ Mcmc.Prior: uniform :: LowerBoundary -> UpperBoundary -> PriorFunction Double
- Mcmc.Proposal: acceptanceRates :: Acceptance k -> Map k Double
+ Mcmc.Proposal: acceptanceRates :: Acceptance k -> Map k (Maybe Double)
- Mcmc.Proposal: createProposal :: PDescription -> (Double -> ProposalSimple a) -> PDimension -> PName -> PWeight -> Tune -> Proposal a
+ Mcmc.Proposal: createProposal :: PDescription -> (TuningParameter -> ProposalSimple a) -> PDimension -> PName -> PWeight -> Tune -> Proposal a
- Mcmc.Proposal: summarizeProposal :: PName -> PDescription -> PWeight -> Maybe Double -> PDimension -> Maybe (Int, Int, Double) -> ByteString
+ Mcmc.Proposal: summarizeProposal :: PName -> PDescription -> PWeight -> Maybe TuningParameter -> PDimension -> Maybe (Int, Int, Double) -> ByteString
- Mcmc.Proposal: tune :: (Double -> Double) -> Proposal a -> Maybe (Proposal a)
+ Mcmc.Proposal: tune :: (TuningParameter -> TuningParameter) -> Proposal a -> Maybe (Proposal a)
- Mcmc.Proposal: tuneCycle :: Map (Proposal a) (Double -> Double) -> Cycle a -> Cycle a
+ Mcmc.Proposal: tuneCycle :: Map (Proposal a) (TuningParameter -> TuningParameter) -> Cycle a -> Cycle a
- Mcmc.Proposal: type ProposalSimple a = a -> GenIO -> IO (a, Log Double, Log Double)
+ Mcmc.Proposal: type ProposalSimple a = a -> GenIO -> IO (a, KernelRatio, Jacobian)
- Mcmc.Proposal.Bactrian: scaleBactrian :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Bactrian: scaleBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Bactrian: slideBactrian :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Bactrian: slideBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Generic: genericContinuous :: (ContDistr d, ContGen d) => d -> (a -> Double -> a) -> Maybe (Double -> Double) -> Maybe (a -> Double -> Log Double) -> ProposalSimple a
+ Mcmc.Proposal.Generic: genericContinuous :: (ContDistr d, ContGen d) => d -> (a -> Double -> a) -> Maybe (Double -> Double) -> Maybe (a -> Double -> Jacobian) -> ProposalSimple a
- Mcmc.Proposal.Scale: scale :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Scale: scale :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Scale: scaleContrarily :: Double -> Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc.Proposal.Scale: scaleContrarily :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc.Proposal.Scale: scaleUnbiased :: Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Scale: scaleUnbiased :: Shape -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Simplex: beta :: Int -> PName -> PWeight -> Tune -> Proposal Simplex
+ Mcmc.Proposal.Simplex: beta :: Dimension -> PName -> PWeight -> Tune -> Proposal Simplex
- Mcmc.Proposal.Simplex: simplexUniform :: Int -> Simplex
+ Mcmc.Proposal.Simplex: simplexUniform :: Dimension -> Simplex
- Mcmc.Proposal.Slide: slide :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Slide: slide :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Slide: slideContrarily :: Double -> Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc.Proposal.Slide: slideContrarily :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc.Proposal.Slide: slideSymmetric :: Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Slide: slideSymmetric :: StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Slide: slideUniformSymmetric :: Double -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Slide: slideUniformSymmetric :: Size -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Settings: Settings :: AnalysisName -> BurnInSpecification -> Iterations -> ExecutionMode -> ParallelizationMode -> SaveMode -> Verbosity -> Settings
+ Mcmc.Settings: Settings :: AnalysisName -> BurnInSpecification -> Iterations -> ExecutionMode -> ParallelizationMode -> SaveMode -> LogMode -> Verbosity -> Settings
Files
- ChangeLog.md +10/−0
- bench/Bench.hs +1/−1
- bench/Normal.hs +12/−8
- bench/Poisson.hs +5/−5
- mcmc.cabal +36/−31
- src/Mcmc.hs +28/−15
- src/Mcmc/Algorithm.hs +5/−1
- src/Mcmc/Algorithm/MC3.hs +86/−30
- src/Mcmc/Algorithm/MHG.hs +283/−0
- src/Mcmc/Algorithm/Metropolis.hs +0/−254
- src/Mcmc/Chain/Chain.hs +12/−17
- src/Mcmc/Chain/Link.hs +7/−5
- src/Mcmc/Chain/Save.hs +20/−16
- src/Mcmc/Chain/Trace.hs +1/−1
- src/Mcmc/Environment.hs +53/−16
- src/Mcmc/Internal/ByteString.hs +1/−1
- src/Mcmc/Internal/Random.hs +1/−1
- src/Mcmc/Internal/Shuffle.hs +1/−1
- src/Mcmc/Likelihood.hs +29/−0
- src/Mcmc/Logger.hs +170/−0
- src/Mcmc/MarginalLikelihood.hs +404/−0
- src/Mcmc/Mcmc.hs +87/−133
- src/Mcmc/Monitor.hs +31/−22
- src/Mcmc/Monitor/Log.hs +1/−1
- src/Mcmc/Monitor/Parameter.hs +1/−1
- src/Mcmc/Monitor/ParameterBatch.hs +1/−1
- src/Mcmc/Monitor/Time.hs +1/−1
- src/Mcmc/Posterior.hs +20/−0
- src/Mcmc/Prior.hs +69/−42
- src/Mcmc/Proposal.hs +229/−124
- src/Mcmc/Proposal/Bactrian.hs +24/−33
- src/Mcmc/Proposal/Generic.hs +2/−2
- src/Mcmc/Proposal/Scale.hs +9/−13
- src/Mcmc/Proposal/Simplex.hs +9/−7
- src/Mcmc/Proposal/Slide.hs +16/−29
- src/Mcmc/Settings.hs +72/−22
- src/Mcmc/Statistics/Types.hs +56/−0
- test/Mcmc/ProposalSpec.hs +7/−7
- test/Mcmc/SaveSpec.hs +8/−8
ChangeLog.md view
@@ -5,6 +5,16 @@ ## Unreleased changes +## 0.5.0.0++- Marginal likelihood estimation using thermodynamic integration or stepping+ stone sampling.+- Various changes of function names (e.g., metropologis -> mhg).+- Updated examples.+- Proper but minimal logging framework.+- Various other changes.++ ## 0.4.0.0 - Greatly improve documentation.
bench/Bench.hs view
@@ -1,6 +1,6 @@ -- | -- Module : Main--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
bench/Normal.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Normal -- Description : Benchmark Metropolis-Hastings-Green algorithm--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -31,7 +31,7 @@ lh = normal trueMean stdDev cc :: Cycle Double-cc = cycleFromList [slideSymmetric 1.0 (PName "Medium") (PWeight 1) Tune]+cc = cycleFromList [slideSymmetric 1.0 (PName "Medium") (pWeight 1) Tune] mons :: [MonitorParameter Double] mons = [monitorDouble "mu"]@@ -52,14 +52,15 @@ Overwrite Sequential NoSave+ LogStdOutOnly Quiet- a <- mhg noPrior lh cc mon 0 g+ a <- mhg noPrior lh cc mon TraceAuto 0 g void $ mcmc s a ccLarge :: Cycle Double ccLarge = cycleFromList- [slideSymmetric 1.0 (PName $ "Medium " ++ show i) (PWeight 1) Tune | i <- [0 .. 100 :: Int]]+ [slideSymmetric 1.0 (PName $ "Medium " ++ show i) (pWeight 1) Tune | i <- [0 .. 100 :: Int]] -- Should have the same run time as 'normalSlide'. normalLargeCycleBench :: GenIO -> IO ()@@ -72,12 +73,13 @@ Overwrite Sequential NoSave+ LogStdOutOnly Quiet- a <- mhg noPrior lh ccLarge mon 0 g+ a <- mhg noPrior lh ccLarge mon TraceAuto 0 g void $ mcmc s a ccBactrian :: Cycle Double-ccBactrian = cycleFromList [slideBactrian 0.5 1.0 (PName "Bactrian") (PWeight 1) Tune]+ccBactrian = cycleFromList [slideBactrian 0.5 1.0 (PName "Bactrian") (pWeight 1) Tune] normalBactrianBench :: GenIO -> IO () normalBactrianBench g = do@@ -89,8 +91,9 @@ Overwrite Sequential NoSave+ LogStdOutOnly Quiet- a <- mhg noPrior lh ccBactrian mon 0 g+ a <- mhg noPrior lh ccBactrian mon TraceAuto 0 g void $ mcmc s a normalMC3 :: GenIO -> Int -> IO ()@@ -103,7 +106,8 @@ Overwrite Sequential NoSave+ LogStdOutOnly Quiet mc3S = MC3Settings (NChains n) (SwapPeriod 2) (NSwaps 1)- a <- mc3 mc3S noPrior lh cc mon 0 g+ a <- mc3 mc3S noPrior lh cc mon TraceAuto 0 g void $ mcmc mcmcS a
bench/Poisson.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Poisson -- Description : Poisson regression model for airline fatalities--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -19,7 +19,6 @@ import Control.Monad import Lens.Micro import Mcmc-import Numeric.Log hiding (sum) import System.Random.MWC type I = (Double, Double)@@ -42,10 +41,10 @@ lh x = product [f ft yr x | (ft, yr) <- zip fatalities normalizedYears] proposalAlpha :: Proposal I-proposalAlpha = _1 @~ slideSymmetric 0.2 (PName "Alpha") (PWeight 1) NoTune+proposalAlpha = _1 @~ slideSymmetric 0.2 (PName "Alpha") (pWeight 1) NoTune proposalBeta :: Proposal I-proposalBeta = _2 @~ slideSymmetric 0.2 (PName "Beta") (PWeight 1) NoTune+proposalBeta = _2 @~ slideSymmetric 0.2 (PName "Beta") (pWeight 1) NoTune proposals :: Cycle I proposals = cycleFromList [proposalAlpha, proposalBeta]@@ -75,6 +74,7 @@ Overwrite Sequential NoSave+ LogStdOutOnly Quiet- a <- mhg noPrior lh proposals mon initial g+ a <- mhg noPrior lh proposals mon TraceAuto initial g void $ mcmc s a
mcmc.cabal view
@@ -1,8 +1,8 @@ cabal-version: 2.2 name: mcmc-version: 0.4.0.0+version: 0.5.0.0 license: GPL-3.0-or-later-copyright: Dominik Schrempf (2020)+copyright: Dominik Schrempf (2021) maintainer: dominik.schrempf@gmail.com author: Dominik Schrempf homepage: https://github.com/dschrempf/mcmc#readme@@ -25,19 +25,23 @@ exposed-modules: Mcmc Mcmc.Algorithm- Mcmc.Algorithm.Metropolis Mcmc.Algorithm.MC3+ Mcmc.Algorithm.MHG Mcmc.Chain.Chain Mcmc.Chain.Link Mcmc.Chain.Save Mcmc.Chain.Trace Mcmc.Environment+ Mcmc.Likelihood+ Mcmc.Logger+ Mcmc.MarginalLikelihood Mcmc.Mcmc Mcmc.Monitor Mcmc.Monitor.Log Mcmc.Monitor.Parameter Mcmc.Monitor.ParameterBatch Mcmc.Monitor.Time+ Mcmc.Posterior Mcmc.Prior Mcmc.Proposal Mcmc.Proposal.Bactrian@@ -46,6 +50,7 @@ Mcmc.Proposal.Slide Mcmc.Proposal.Simplex Mcmc.Settings+ Mcmc.Statistics.Types hs-source-dirs: src other-modules:@@ -58,27 +63,27 @@ default-language: Haskell2010 ghc-options: -Wall -Wunused-packages build-depends:- aeson >=1.5.4.1 && <1.6,+ aeson >=1.5.6.0, base >=4.7 && <5,- bytestring >=0.10.10.0 && <0.11,- circular >=0.3.1.1 && <0.4,- containers >=0.6.2.1 && <0.7,- data-default >=0.7.1.1 && <0.8,- deepseq >=1.4.4.0 && <1.5,- directory >=1.3.6.0 && <1.4,- dirichlet >=0.1.0.0 && <0.2,- double-conversion >=2.0.2.0 && <2.1,- log-domain ==0.13.*,- microlens >=0.4.11.2 && <0.5,- mwc-random >=0.14.0.0 && <0.15,- monad-parallel >=0.7.2.3 && <0.8,- pretty-show ==1.10.*,- primitive >=0.7.1.0 && <0.8,- statistics >=0.15.2.0 && <0.16,- time >=1.9.3 && <1.10,- transformers >=0.5.6.2 && <0.6,- vector >=0.12.1.2 && <0.13,- zlib >=0.6.2.2 && <0.7+ bytestring >=0.10.12.0,+ circular >=0.4.0.0,+ containers >=0.6.2.1,+ data-default >=0.7.1.1,+ deepseq >=1.4.4.0,+ directory >=1.3.6.0,+ dirichlet >=0.1.0.4,+ double-conversion >=2.0.2.0,+ log-domain >=0.13.1,+ microlens >=0.4.12.0,+ mwc-random >=0.15.0.1,+ monad-parallel >=0.7.2.4,+ pretty-show >=1.10,+ primitive >=0.7.1.0,+ statistics >=0.15.2.0,+ time >=1.9.3,+ transformers >=0.5.6.2,+ vector >=0.12.3.0,+ zlib >=0.6.2.3 test-suite mcmc-test type: exitcode-stdio-1.0@@ -93,11 +98,11 @@ ghc-options: -Wall -Wunused-packages build-depends: base >=4.7 && <5,- hspec >=2.7.4 && <2.8,- log-domain ==0.13.*,+ hspec >=2.7.10,+ log-domain >=0.13.1, mcmc -any,- mwc-random >=0.14.0.0 && <0.15,- statistics >=0.15.2.0 && <0.16+ mwc-random >=0.15.0.1,+ statistics >=0.15.2.0 benchmark mcmc-bench type: exitcode-stdio-1.0@@ -112,8 +117,8 @@ ghc-options: -Wall -Wunused-packages build-depends: base >=4.7 && <5,- criterion >=1.5.7.0 && <1.6,- log-domain ==0.13.*,+ criterion >=1.5.9.0,+ log-domain >=0.13.1, mcmc -any,- microlens >=0.4.11.2 && <0.5,- mwc-random >=0.14.0.0 && <0.15+ microlens >=0.4.12.0,+ mwc-random >=0.15.0.1
src/Mcmc.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc -- Description : Markov chain Monte Carlo samplers, batteries included--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -76,9 +76,13 @@ -- Gibbs sampler, or a random sequence scan Gibbs sampler, respectively. See -- 'Order'. --- -- Note that it is of utter importance that the given 'Cycle' enables- -- traversal of the complete state space. Otherwise, the Markov chain will- -- not converge to the correct stationary posterior distribution.+ -- Notes:+ -- - It is important that the given 'Cycle' enables traversal of the+ -- complete state space. Otherwise, the Markov chain will not converge to+ -- the correct stationary posterior distribution.+ -- - Be careful when assigning proposals because acceptance ratios may have+ -- to be amended using Jacobians. Please see an [example involving a pair+ -- of numbers](https://github.com/dschrempf/mcmc/blob/master/mcmc-examples/Pair/Pair.hs). -- -- Proposals are named according to what they do, i.e., how they change the -- state of a Markov chain, and not according to the intrinsically used@@ -114,8 +118,12 @@ -- http://dx.doi.org/10.1093/sysbio/syw021 PName (..), PWeight (..),+ pWeight, Proposal,+ JacobianFunction, (@~),+ liftProposal,+ liftProposalWith, Tune (..), scale, scaleUnbiased,@@ -157,36 +165,39 @@ monitorFile, MonitorBatch, monitorBatch,+ simpleMonitor, module Mcmc.Monitor.Parameter, module Mcmc.Monitor.ParameterBatch, - -- * Prior distributions-- -- | Convenience functions for computing priors.+ -- * Prior, likelihood, and posterior values and functions module Mcmc.Prior,+ module Mcmc.Likelihood, -- * MCMC samplers mcmc, mcmcContinue,- -- | See also 'settingsLoad', 'mhgLoad', and 'mc3Load'. -- * Algorithms- module Mcmc.Algorithm.Metropolis,+ module Mcmc.Algorithm.MHG, module Mcmc.Algorithm.MC3, + -- * Marginal likelihood calculation+ module Mcmc.MarginalLikelihood, - -- * Useful type synonyms- PriorFunction,- noPrior,- LikelihoodFunction,- noLikelihood,+ -- * Useful types+ module Mcmc.Statistics.Types,++ -- * Useful re-exports+ module Numeric.Log, ) where import Mcmc.Algorithm.MC3-import Mcmc.Algorithm.Metropolis+import Mcmc.Algorithm.MHG import Mcmc.Chain.Chain+import Mcmc.Likelihood+import Mcmc.MarginalLikelihood import Mcmc.Mcmc import Mcmc.Monitor import Mcmc.Monitor.Parameter@@ -198,3 +209,5 @@ import Mcmc.Proposal.Simplex import Mcmc.Proposal.Slide import Mcmc.Settings+import Mcmc.Statistics.Types+import Numeric.Log hiding (sum)
src/Mcmc/Algorithm.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Algorithm -- Description : Algortihms for Markov chain Monte Carlo samplers--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -25,6 +25,10 @@ -- | Current iteration. aIteration :: a -> Int++ -- | Check if the current state is invalid. At the moment this should just+ -- check if the posterior probability is zero or NaN.+ aIsInValidState :: a -> Bool -- | Sample the next state. aIterate :: ParallelizationMode -> a -> IO a
src/Mcmc/Algorithm/MC3.hs view
@@ -5,7 +5,7 @@ -- | -- Module : Mcmc.Algorithm.MC3 -- Description : Metropolis-coupled Markov chain Monte Carlo algorithm--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -19,8 +19,8 @@ -- Also known as parallel tempering. -- -- Like any other parallel MCMC algorithm, the 'MC3' algorithm is essentially an--- 'Mcmc.Algorithm.Metropolis.MHG' algorithm on the product space of all--- parallel chains.+-- 'Mcmc.Algorithm.MHG.MHG' algorithm on the product space of all parallel+-- chains. -- -- For example, see --@@ -64,7 +64,7 @@ import Data.Word -- import Debug.Trace hiding (trace) import Mcmc.Algorithm-import Mcmc.Algorithm.Metropolis+import Mcmc.Algorithm.MHG import Mcmc.Chain.Chain import Mcmc.Chain.Link import Mcmc.Chain.Save@@ -72,11 +72,14 @@ import Mcmc.Internal.Random import Mcmc.Internal.Shuffle import Mcmc.Monitor+import Mcmc.Posterior import Mcmc.Proposal import Mcmc.Settings import Numeric.Log hiding (sum) import System.Random.MWC +-- import Debug.Trace hiding (trace)+ -- | Total number of parallel chains. -- -- Must be two or larger.@@ -146,9 +149,17 @@ SavedMC3 a -> IO (MC3 a) fromSavedMC3 pr lh cc mn (SavedMC3 s scs bs i ac g') = do- mhgs <- V.mapM (fmap MHG . fromSavedChain pr lh cc mn) scs+ mhgs <-+ V.fromList+ <$> sequence+ [ MHG <$> fromSavedChain pf lf cc mn sc+ | (sc, pf, lf) <- zip3 (V.toList scs) prs lhs+ ] g <- loadGen g' return $ MC3 s mhgs bs i ac g+ where+ prs = map (heatFunction pr) $ U.toList bs+ lhs = map (heatFunction lh) $ U.toList bs -- | The MC3 algorithm. data MC3 a = MC3@@ -167,6 +178,7 @@ instance ToJSON a => Algorithm (MC3 a) where aName = const "Metropolis-coupled Markov chain Monte Carlo (MC3)" aIteration = mc3Iteration+ aIsInValidState = mc3IsInValidState aIterate = mc3Iterate aAutoTune = mc3AutoTune aResetAcceptance = mc3ResetAcceptance@@ -177,6 +189,20 @@ aCloseMonitors = mc3CloseMonitors aSave = mc3Save +heatFunction ::+ -- Cold Function.+ (a -> Log Double) ->+ -- Reciprocal temperature.+ Double ->+ -- The heated prior or likelihood function+ (a -> Log Double)+heatFunction f b+ | b <= 0 = error "heatFunction: Reciprocal temperature is zero or negative."+ | b == 1.0 = f+ | otherwise = (** b') . f+ where+ b' = Exp $ log b+ -- The prior and likelihood values of the current link are updated. -- -- NOTE: The trace is not changed! In particular, the prior and likelihood@@ -191,7 +217,7 @@ Double -> MHG a -> MHG a-setReciprocalTemperature prf lhf beta a =+setReciprocalTemperature coldPrf coldLhf b a = MHG $ c { priorFunction = prf',@@ -200,15 +226,14 @@ } where c = fromMHG a- b' = Exp $ log beta -- We need twice the amount of computations compared to taking the power -- after calculating the posterior (pr x * lh x) ** b'. However, I don't -- think this is a serious problem. -- -- To minimize computations, it is key to avoid modification of the -- reciprocal temperature for the cold chain.- prf' = (** b') . prf- lhf' = (** b') . lhf+ prf' = heatFunction coldPrf b+ lhf' = heatFunction coldLhf b x = state $ link c initMHG ::@@ -224,14 +249,14 @@ IO (MHG a) initMHG prf lhf i beta a | i < 0 = error "initMHG: Chain index negative."- -- Do not temper with the cold chain.- | i == 0 = return a+ -- Only set the id for the cold chain.+ | i == 0 = return $ MHG $ c {chainId = Just 0} | otherwise = do -- We have to push the current link in the trace, since it is not set by -- 'setReciprocalTemperature'. The other links in the trace are still -- pointing to the link of the cold chain, but this has no effect. t' <- pushT l t- return $ MHG $ c {chainId = i, trace = t'}+ return $ MHG $ c {chainId = Just i, trace = t'} where a' = setReciprocalTemperature prf lhf beta a c = fromMHG a'@@ -254,17 +279,18 @@ LikelihoodFunction a -> Cycle a -> Monitor a ->- a ->+ TraceLength ->+ InitialState a -> GenIO -> IO (MC3 a)-mc3 s pr lh cc mn i0 g+mc3 s pr lh cc mn tr i0 g | n < 2 = error "mc3: The number of chains must be two or larger." | sp < 1 = error "mc3: The swap period must be strictly positive." | sn < 1 || sn > n - 1 = error "mc3: The number of swaps must be in [1, NChains - 1]." | otherwise = do -- Split random number generators. gs <- V.fromList <$> splitGen n g- cs <- V.mapM (mhg pr lh cc mn i0) gs+ cs <- V.mapM (mhg pr lh cc mn tr i0) gs hcs <- V.izipWithM (initMHG pr lh) (V.convert bs) cs return $ MC3 s hcs bs 0 (emptyA [0 .. n - 2]) g where@@ -276,10 +302,10 @@ -- -- NOTE: Have to 'take n' elements, because vectors are not as lazy as -- lists.- bs = U.fromList $ take n $ iterate (* 0.92) 1.0+ bs = U.fromList $ take n $ iterate (* 0.97) 1.0 mc3Fn :: AnalysisName -> FilePath-mc3Fn (AnalysisName nm) = nm ++ ".mc3"+mc3Fn (AnalysisName nm) = nm ++ ".mcmc.mc3" -- | Save an MC3 algorithm. mc3Save ::@@ -315,7 +341,7 @@ -- Index j>=0, j/=i of right chain. Int -> MHGChains a ->- (MHGChains a, Log Double)+ (MHGChains a, Posterior) swapWith i j xs | i < 0 = error "swapWith: Left index is negative." | j < 0 = error "swapWith: Right index is negative."@@ -356,12 +382,28 @@ Int -> IO (MC3 a) mc3ProposeSwap a i = do+ let cs = mc3MHGChains a+ -- -- Debug.+ -- prL = prior $ link $ fromMHG $ cs V.! i+ -- prR = prior $ link $ fromMHG $ cs V.! (i+1)+ -- lhL = likelihood $ link $ fromMHG $ cs V.! i+ -- lhR = likelihood $ link $ fromMHG $ cs V.! (i+1) -- 1. Sample new state and get the Metropolis ratio.- let (!y, !r) = swapWith i (i + 1) $ mc3MHGChains a+ let (!y, !r) = swapWith i (i + 1) cs -- 2. Accept or reject. accept <- mhgAccept r g if accept then do+ -- -- Debug.+ -- traceIO $ "Swap accepted: " <> show i <> " <-> " <> show (i+1)+ -- let prL' = prior $ link $ fromMHG $ y V.! i+ -- prR' = prior $ link $ fromMHG $ y V.! (i+1)+ -- lhL' = likelihood $ link $ fromMHG $ y V.! i+ -- lhR' = likelihood $ link $ fromMHG $ y V.! (i+1)+ -- traceIO $ "Log priors (left, right, before swap): " <> show (ln prL) <> " " <> show (ln prR)+ -- traceIO $ "Log priors (left, right, after swap): " <> show (ln prL') <> " " <> show (ln prR')+ -- traceIO $ "Log likelihoods (left, right, before swap): " <> show (ln lhL) <> " " <> show (ln lhR)+ -- traceIO $ "Log likelihood (left, right, after swap): " <> show (ln lhL') <> " " <> show (ln lhR') let !ac' = pushA i True (mc3SwapAcceptance a) return $ a {mc3MHGChains = y, mc3SwapAcceptance = ac'} else do@@ -370,9 +412,15 @@ where g = mc3Generator a +mc3IsInValidState :: ToJSON a => MC3 a -> Bool+mc3IsInValidState a = V.any aIsInValidState mhgs+ where+ mhgs = mc3MHGChains a+ -- TODO: Splimix. 'mc3Iterate' is actually not parallel, but concurrent because -- of the IO constraint. Use pure parallel code when we have a pure generator.--- However, we have honor the mutable traces.+--+-- However, we to have honor the mutable traces. mc3Iterate :: ToJSON a => ParallelizationMode ->@@ -399,7 +447,7 @@ Parallel -> -- See 'Control.Monad.Parallel' of package 'monad-parallel'. Go via a -- list, and use 'forkIO'.- V.fromList <$> P.mapM (aIterate pm) (V.toList (mc3MHGChains a'))+ V.fromList <$> P.mapM (aIterate pm) (V.toList $ mc3MHGChains a') let i = mc3Iteration a' return $ a' {mc3MHGChains = mhgs, mc3Iteration = succ i} @@ -432,7 +480,7 @@ mhgs' = V.map aAutoTune mhgs -- 2. Auto tune temperatures. optimalRate = getOptimalRate PDimensionUnknown- currentRates = acceptanceRates $ mc3SwapAcceptance a+ mCurrentRates = acceptanceRates $ mc3SwapAcceptance a -- We assume that the acceptance rate of state swaps between two chains is -- roughly proportional to the ratio of the temperatures of the chains. -- Hence, we focus on temperature ratios, actually reciprocal temperature@@ -442,7 +490,9 @@ -- -- The factor (1/2) was determined by a few tests and is otherwise -- absolutely arbitrary.- xi i = exp $ (/ 2) $ (currentRates M.! i) - optimalRate+ xi i = case mCurrentRates M.! i of+ Nothing -> 1.0+ Just currentRate -> exp $ (/ 2) $ currentRate - optimalRate bs = mc3ReciprocalTemperatures a n = fromNChains $ mc3NChains $ mc3Settings a -- Do not change the temperature, and the prior and likelihood functions of@@ -481,9 +531,12 @@ [ "MC3: Cycle of cold chain.", coldMHGCycleSummary ]- ++ [ "MC3: Average acceptance rate across all chains: " <> BL.fromStrict (BC.toFixed 2 ar)- | not $ isNaN ar- ]+ ++ case mAr of+ Nothing -> []+ Just ar ->+ [ "MC3: Average acceptance rate across all chains: "+ <> BL.fromStrict (BC.toFixed 2 ar)+ ] ++ [ "MC3: Reciprocal temperatures of the chains: " <> BL.intercalate ", " bsB <> ".", "MC3: Summary of state swaps.", "MC3: The swap period is " <> swapPeriodB <> ".",@@ -494,7 +547,7 @@ ++ [ summarizeProposal (PName $ show i ++ " <-> " ++ show (i + 1)) (PDescription "Swap states between chains")- (PWeight 1)+ (pWeight 1) (Just $ bs U.! (i + 1)) PDimensionUnknown (acceptanceRate i swapAcceptance)@@ -505,9 +558,12 @@ mhgs = mc3MHGChains a coldMHGCycleSummary = aSummarizeCycle $ V.head mhgs cs = V.map fromMHG mhgs- as = V.map (acceptanceRates . acceptance) cs- vAr = V.map (\m -> sum m / fromIntegral (length m)) as- ar = V.sum vAr / fromIntegral (V.length vAr)+ -- Acceptance rates may be 'Nothing' when no proposals have been undertaken.+ -- The 'sequence' operations pull the 'Nothing's out of the inner+ -- structures.+ as = sequence $ V.map (sequence . acceptanceRates . acceptance) cs+ mVecAr = V.map (\mp -> sum mp / fromIntegral (length mp)) <$> as+ mAr = (\vec -> V.sum vec / fromIntegral (V.length vec)) <$> mVecAr bs = mc3ReciprocalTemperatures a bsB = map (BL.fromStrict . BC.toFixed 2) $ U.toList bs swapPeriod = fromSwapPeriod $ mc3SwapPeriod $ mc3Settings a
+ src/Mcmc/Algorithm/MHG.hs view
@@ -0,0 +1,283 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module : Mcmc.Algorithm.MHG+-- Description : Metropolis-Hastings-Green algorithm+-- Copyright : (c) Dominik Schrempf 2021+-- License : GPL-3.0-or-later+--+-- Maintainer : dominik.schrempf@gmail.com+-- Stability : unstable+-- Portability : portable+--+-- Creation date: Tue May 5 20:11:30 2020.+--+-- The Metropolis-Hastings-Green ('MHG') algorithm.+--+-- For example, see Geyer, C. J., Introduction to Markov chain Monte Carlo, In+-- Handbook of Markov Chain Monte Carlo (pp. 45) (2011). CRC press.+module Mcmc.Algorithm.MHG+ ( MHG (..),+ mhg,+ mhgSave,+ mhgLoad,+ MHGRatio,+ mhgAccept,+ )+where++import Codec.Compression.GZip+import Control.Monad+import Control.Monad.IO.Class+import Data.Aeson+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Time+import Mcmc.Algorithm+import Mcmc.Chain.Chain+import Mcmc.Chain.Link+import Mcmc.Chain.Save+import Mcmc.Chain.Trace+import Mcmc.Monitor+import Mcmc.Posterior+import Mcmc.Proposal+import Mcmc.Settings+import Numeric.Log+import System.Random.MWC+import Text.Printf+import Prelude hiding (cycle)++-- | The MHG algorithm.+newtype MHG a = MHG {fromMHG :: Chain a}++instance ToJSON a => Algorithm (MHG a) where+ aName = const "Metropolis-Hastings-Green (MHG)"+ aIteration = iteration . fromMHG+ aIsInValidState = mhgIsInValidState+ aIterate = mhgIterate+ aAutoTune = mhgAutoTune+ aResetAcceptance = mhgResetAcceptance+ aSummarizeCycle = mhgSummarizeCycle+ aOpenMonitors = mhgOpenMonitors+ aExecuteMonitors = mhgExecuteMonitors+ aStdMonitorHeader = mhgStdMonitorHeader+ aCloseMonitors = mhgCloseMonitors+ aSave = mhgSave++-- NOTE: IO is required because the trace is mutable.++-- | Initialize an MHG algorithm.+mhg ::+ PriorFunction a ->+ LikelihoodFunction a ->+ Cycle a ->+ Monitor a ->+ TraceLength ->+ InitialState a ->+ GenIO ->+ IO (MHG a)+mhg pr lh cc mn trLen i0 g = do+ -- The trace is a mutable vector and the mutable state needs to be handled by+ -- a monad.+ tr <- replicateT traceLength l0+ return $ MHG $ Chain Nothing l0 0 tr ac g 0 pr lh cc mn+ where+ l0 = Link i0 (pr i0) (lh i0)+ ac = emptyA $ ccProposals cc+ batchMonitorSizes = map getMonitorBatchSize $ mBatches mn+ minimumTraceLength = case trLen of+ TraceAuto -> 1+ TraceMinimum n -> n+ traceLength = maximum $ minimumTraceLength : batchMonitorSizes++mhgFn :: AnalysisName -> FilePath+mhgFn (AnalysisName nm) = nm ++ ".mcmc.mhg"++-- | Save an MHG algorithm.+mhgSave ::+ ToJSON a =>+ AnalysisName ->+ MHG a ->+ IO ()+mhgSave nm (MHG c) = do+ savedChain <- toSavedChain c+ BL.writeFile (mhgFn nm) $ compress $ encode savedChain++-- | Load an MHG algorithm.+--+-- See 'Mcmc.Mcmc.mcmcContinue'.+mhgLoad ::+ FromJSON a =>+ PriorFunction a ->+ LikelihoodFunction a ->+ Cycle a ->+ Monitor a ->+ AnalysisName ->+ IO (MHG a)+mhgLoad pr lh cc mn nm = do+ savedChain <- eitherDecode . decompress <$> BL.readFile (mhgFn nm)+ chain <- either error (fromSavedChain pr lh cc mn) savedChain+ return $ MHG chain++-- | MHG ratios are stored in log domain.+type MHGRatio = Log Double++-- The MHG ratio. This implementation has the following properties:+--+-- - The ratio is 'Infinity' if fX is zero. In this case, the proposal is always+-- accepted.+--+-- - The ratio 'NaN' if (fY or q or j) and fX are zero. In this case, the+-- proposal is always rejected.+--+-- This means that a chain in a state with posterior probability zero (fX=0) can+-- only move if a state with non-zero posterior probability is proposed.+-- Otherwise it is stuck. Therefore, I print a warning when the posterior+-- probability is zero in the beginning of the MCMC run. This is probably not+-- the best behavior, but see below.+--+-- 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 [1],+-- [2]), or (b) almost surely reject the proposal when either fY or q are zero+-- (Chapter 1 of [1]).+--+-- Since I trust the author of Chapter 1 (Charles Geyer) I choose to follow+-- option (b). However, Option (a) is more user-friendly.+--+-- [1] Handbook of markov chain monte carlo (2011), CRC press.+--+-- [2] Dellaportas, P., & Roberts, G. O., An introduction to mcmc, Lecture Notes+-- in Statistics, (), 1–41 (2003).+-- http://dx.doi.org/10.1007/978-0-387-21811-3_1.+mhgRatio :: Posterior -> Posterior -> KernelRatio -> Jacobian -> MHGRatio+-- q = qYX / qXY * jXY; see 'ProposalSimple'.+-- j = Jacobian.+mhgRatio fX fY q j = fY / fX * q * j+{-# INLINE mhgRatio #-}++-- | Accept or reject a proposal with given MHG ratio?+mhgAccept :: MHGRatio -> GenIO -> IO Bool+mhgAccept r g+ | ln r >= 0.0 = return True+ | otherwise = do+ b <- uniform g+ return $ b < exp (ln r)++mhgPropose :: MHG a -> Proposal a -> IO (MHG a)+mhgPropose (MHG c) p = do+ -- 1. Sample new state.+ (!y, !q, !j) <- liftIO $ s x g+ -- 2. Calculate Metropolis-Hastings-Green ratio.+ let !pY = pF y+ !lY = lF y+ !r = mhgRatio (pX * lX) (pY * lY) q j+ -- 3. Accept or reject.+ -- if ln r >= 0.0+ -- then do+ -- let !ac' = pushA p True ac+ -- return $ MHG $ c {link = Link y pY lY, acceptance = ac'}+ -- else do+ -- b <- uniform g+ -- if b < exp (ln r)+ -- then do+ -- let !ac' = pushA p True ac+ -- return $ MHG $ c {link = Link y pY lY, acceptance = ac'}+ -- else do+ -- let !ac' = pushA p False ac+ -- return $ MHG $ c {acceptance = pushA p False ac'}+ accept <- mhgAccept r g+ if accept+ then do+ let !ac' = pushA p True ac+ return $ MHG $ c {link = Link y pY lY, acceptance = ac'}+ else do+ let !ac' = pushA p False ac+ return $ MHG $ c {acceptance = pushA p False ac'}+ where+ s = prSimple p+ (Link x pX lX) = link c+ pF = priorFunction c+ lF = likelihoodFunction c+ ac = acceptance c+ g = generator c++mhgPush :: MHG a -> IO (MHG a)+mhgPush (MHG c) = do+ t' <- pushT i t+ return $ MHG c {trace = t', iteration = succ n}+ where+ i = link c+ t = trace c+ n = iteration c++mhgIsInValidState :: MHG a -> Bool+mhgIsInValidState a = ((p * l) == 0) || (p * l == (0 / 0))+ where+ x = link $ fromMHG a+ p = prior x+ l = likelihood x++-- Ignore the number of capabilities. I have tried a lot of stuff, but the MHG+-- algorithm is just inherently sequential. Parallelization can be achieved by+-- having parallel prior and/or likelihood functions, or by using algorithms+-- running parallel chains such as 'MC3'.+mhgIterate :: ParallelizationMode -> MHG a -> IO (MHG a)+mhgIterate _ a = do+ ps <- prepareProposals cc g+ a' <- foldM mhgPropose a ps+ mhgPush a'+ where+ c = fromMHG a+ cc = cycle c+ g = generator c++mhgAutoTune :: MHG a -> MHG a+mhgAutoTune (MHG c) = MHG $ c {cycle = autoTuneCycle ac cc}+ where+ ac = acceptance c+ cc = cycle c++mhgResetAcceptance :: MHG a -> MHG a+mhgResetAcceptance (MHG c) = MHG $ c {acceptance = resetA ac}+ where+ ac = acceptance c++mhgSummarizeCycle :: MHG a -> BL.ByteString+mhgSummarizeCycle (MHG c) = summarizeCycle ac cc+ where+ cc = cycle c+ ac = acceptance c++mhgOpenMonitors :: AnalysisName -> ExecutionMode -> MHG a -> IO (MHG a)+mhgOpenMonitors nm em (MHG c) = do+ m' <- mOpen pre suf em m+ return $ MHG c {monitor = m'}+ where+ m = monitor c+ pre = fromAnalysisName nm+ suf = maybe "" (printf "%02d") $ chainId c++mhgExecuteMonitors ::+ Verbosity ->+ -- Starting time.+ UTCTime ->+ -- Total number of iterations.+ Int ->+ MHG a ->+ IO (Maybe BL.ByteString)+mhgExecuteMonitors vb t0 iTotal (MHG c) = mExec vb i i0 t0 tr iTotal m+ where+ i = iteration c+ i0 = start c+ tr = trace c+ m = monitor c++mhgStdMonitorHeader :: MHG a -> BL.ByteString+mhgStdMonitorHeader (MHG c) = msHeader (mStdOut $ monitor c)++mhgCloseMonitors :: MHG a -> IO (MHG a)+mhgCloseMonitors (MHG c) = do+ m' <- mClose m+ return $ MHG $ c {monitor = m'}+ where+ m = monitor c
− src/Mcmc/Algorithm/Metropolis.hs
@@ -1,254 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE OverloadedStrings #-}---- |--- Module : Mcmc.Algorithm.Metropolis--- Description : Metropolis-Hastings-Green algorithm--- Copyright : (c) Dominik Schrempf 2020--- License : GPL-3.0-or-later------ Maintainer : dominik.schrempf@gmail.com--- Stability : unstable--- Portability : portable------ Creation date: Tue May 5 20:11:30 2020.------ The Metropolis-Hastings-Green ('MHG') algorithm.------ For example, see Geyer, C. J., Introduction to Markov chain Monte Carlo, In--- Handbook of Markov Chain Monte Carlo (pp. 45) (2011). CRC press.-module Mcmc.Algorithm.Metropolis- ( MHG (..),- mhg,- mhgSave,- mhgLoad,- mhgAccept,- )-where--import Codec.Compression.GZip-import Control.Monad-import Control.Monad.IO.Class-import Data.Aeson-import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Time-import Mcmc.Algorithm-import Mcmc.Chain.Chain-import Mcmc.Chain.Link-import Mcmc.Chain.Save-import Mcmc.Chain.Trace-import Mcmc.Monitor-import Mcmc.Proposal-import Mcmc.Settings-import Numeric.Log-import System.Random.MWC-import Text.Printf-import Prelude hiding (cycle)---- | The MHG algorithm.-newtype MHG a = MHG {fromMHG :: Chain a}--instance ToJSON a => Algorithm (MHG a) where- aName = const "Metropolis-Hastings-Green (MHG)"- aIteration = iteration . fromMHG- aIterate = mhgIterate- aAutoTune = mhgAutoTune- aResetAcceptance = mhgResetAcceptance- aSummarizeCycle = mhgSummarizeCycle- aOpenMonitors = mhgOpenMonitors- aExecuteMonitors = mhgExecuteMonitors- aStdMonitorHeader = mhgStdMonitorHeader- aCloseMonitors = mhgCloseMonitors- aSave = mhgSave---- NOTE: IO is required because the trace is mutable.---- | Initialize an MHG algorithm.-mhg ::- PriorFunction a ->- LikelihoodFunction a ->- Cycle a ->- Monitor a ->- -- | The initial state in the state space @a@.- a ->- -- | A source of randomness. For reproducible runs, make sure to use- -- generators with the same seed.- GenIO ->- IO (MHG a)-mhg pr lh cc mn i0 g = do- -- The trace is a mutable vector and the mutable state needs to be handled by- -- a monad.- tr <- replicateT traceLength l0- return $ MHG $ Chain 0 l0 0 tr ac g 0 pr lh cc mn- where- l0 = Link i0 (pr i0) (lh i0)- ac = emptyA $ ccProposals cc- batchMonitorSizes = map getMonitorBatchSize $ mBatches mn- traceLength = maximum $ 1 : batchMonitorSizes--mhgFn :: AnalysisName -> FilePath-mhgFn (AnalysisName nm) = nm ++ ".mhg"---- | Save an MHG algorithm.-mhgSave ::- ToJSON a =>- AnalysisName ->- MHG a ->- IO ()-mhgSave nm (MHG c) = do- savedChain <- toSavedChain c- BL.writeFile (mhgFn nm) $ compress $ encode savedChain---- | Load an MHG algorithm.------ See 'Mcmc.Mcmc.mcmcContinue'.-mhgLoad ::- FromJSON a =>- PriorFunction a ->- LikelihoodFunction a ->- Cycle a ->- Monitor a ->- AnalysisName ->- IO (MHG a)-mhgLoad pr lh cc mn nm = do- savedChain <- eitherDecode . decompress <$> BL.readFile (mhgFn nm)- chain <- either error (fromSavedChain pr lh cc mn) savedChain- return $ MHG chain---- The MHG ratio.------ '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).-mhgRatio :: Log Double -> Log Double -> Log Double -> Log Double -> Log Double--- q = qYX / qXY * jXY; see 'ProposalSimple'.--- j = Jacobian.-mhgRatio fX fY q j = fY / fX * q * j-{-# INLINE mhgRatio #-}---- | Accept or reject a proposal with given MHG ratio?-mhgAccept :: Log Double -> GenIO -> IO Bool-mhgAccept r g- | ln r >= 0.0 = return True- | otherwise = do- b <- uniform g- return $ b < exp (ln r)--mhgPropose :: MHG a -> Proposal a -> IO (MHG a)-mhgPropose (MHG c) p = do- -- 1. Sample new state.- (!y, !q, !j) <- liftIO $ s x g- -- 2. Calculate Metropolis-Hastings-Green ratio.- let !pY = pF y- !lY = lF y- !r = mhgRatio (pX * lX) (pY * lY) q j- -- 3. Accept or reject.- -- if ln r >= 0.0- -- then do- -- let !ac' = pushA p True ac- -- return $ MHG $ c {link = Link y pY lY, acceptance = ac'}- -- else do- -- b <- uniform g- -- if b < exp (ln r)- -- then do- -- let !ac' = pushA p True ac- -- return $ MHG $ c {link = Link y pY lY, acceptance = ac'}- -- else do- -- let !ac' = pushA p False ac- -- return $ MHG $ c {acceptance = pushA p False ac'}- accept <- mhgAccept r g- if accept- then do- let !ac' = pushA p True ac- return $ MHG $ c {link = Link y pY lY, acceptance = ac'}- else do- let !ac' = pushA p False ac- return $ MHG $ c {acceptance = pushA p False ac'}- where- s = pSimple p- (Link x pX lX) = link c- pF = priorFunction c- lF = likelihoodFunction c- ac = acceptance c- g = generator c--mhgPush :: MHG a -> IO (MHG a)-mhgPush (MHG c) = do- t' <- pushT i t- return $ MHG c {trace = t', iteration = succ n}- where- i = link c- t = trace c- n = iteration c---- Ignore the number of capabilities. I have tried a lot of stuff, but the MHG--- algorithm is just inherently sequential. Parallelization can be achieved by--- having parallel prior and/or likelihood functions, or by using algorithms--- running parallel chains such as 'MC3'.-mhgIterate :: ParallelizationMode -> MHG a -> IO (MHG a)-mhgIterate _ a = do- ps <- orderProposals cc g- a' <- foldM mhgPropose a ps- mhgPush a'- where- c = fromMHG a- cc = cycle c- g = generator c--mhgAutoTune :: MHG a -> MHG a-mhgAutoTune (MHG c) = MHG $ c {cycle = autoTuneCycle ac cc}- where- ac = acceptance c- cc = cycle c--mhgResetAcceptance :: MHG a -> MHG a-mhgResetAcceptance (MHG c) = MHG $ c {acceptance = resetA ac}- where- ac = acceptance c--mhgSummarizeCycle :: MHG a -> BL.ByteString-mhgSummarizeCycle (MHG c) = summarizeCycle ac cc- where- cc = cycle c- ac = acceptance c--mhgOpenMonitors :: AnalysisName -> ExecutionMode -> MHG a -> IO (MHG a)-mhgOpenMonitors nm em (MHG c) = do- m' <- mOpen pre suf em m- return $ MHG c {monitor = m'}- where- m = monitor c- pre = fromAnalysisName nm- suf = printf "%02d" $ chainId c--mhgExecuteMonitors ::- Verbosity ->- -- Starting time.- UTCTime ->- -- Total number of iterations.- Int ->- MHG a ->- IO (Maybe BL.ByteString)-mhgExecuteMonitors vb t0 iTotal (MHG c) = mExec vb i i0 t0 tr iTotal m- where- i = iteration c- i0 = start c- tr = trace c- m = monitor c--mhgStdMonitorHeader :: MHG a -> BL.ByteString-mhgStdMonitorHeader (MHG c) = msHeader (mStdOut $ monitor c)--mhgCloseMonitors :: MHG a -> IO (MHG a)-mhgCloseMonitors (MHG c) = do- m' <- mClose m- return $ MHG $ c {monitor = m'}- where- m = monitor c
src/Mcmc/Chain/Chain.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Chain.Chain -- Description : Simple representation of a Markov chain--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -14,35 +14,29 @@ noPrior, LikelihoodFunction, noLikelihood,+ InitialState, Chain (..), ) where --- Note: It is not necessary to add another type @b@ to store supplementary+-- NOTE: It is not necessary to add another type @b@ to store supplementary -- information about the chain. The information can just be stored in @a@ -- equally well. +-- TODO: First class parameters. Make a type class for parameter types (name,+-- lens, proposals, monitors).+ import Mcmc.Chain.Link import Mcmc.Chain.Trace+import Mcmc.Likelihood import Mcmc.Monitor+import Mcmc.Prior import Mcmc.Proposal-import Numeric.Log import System.Random.MWC hiding (save) import Prelude hiding (cycle) --- | Prior function.-type PriorFunction a = a -> Log Double---- | Flat prior function. Useful for testing and debugging.-noPrior :: PriorFunction a-noPrior = const 1.0---- | Likelihood function.-type LikelihoodFunction a = a -> Log Double---- | Flat likelihood function. Useful for testing and debugging.-noLikelihood :: LikelihoodFunction a-noLikelihood = const 1.0+-- | Type synonym to indicate the initial state.+type InitialState a = a -- | The chain contains all information to run an MCMC sampler. --@@ -60,8 +54,9 @@ -- The 'Mcmc.Environment.Environment' of the chain is not stored externally. data Chain a = Chain { -- Variables; saved.+ -- | Chain index; useful if more chains are run.- chainId :: Int,+ chainId :: Maybe Int, -- | The current 'Link' of the chain combines the current state and the -- current likelihood. The link is updated after a proposal has been -- executed.
src/Mcmc/Chain/Link.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Chain.Link -- Description : The state combined with auxiliary variables--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -18,6 +18,8 @@ import Data.Aeson import Data.Aeson.Types+import Mcmc.Likelihood+import Mcmc.Prior import Numeric.Log -- | Link of a Markov chain. For reasons of computational efficiency, each state@@ -25,10 +27,10 @@ data Link a = Link { -- | The current state in the state space @a@. state :: a,- -- | The current prior.- prior :: Log Double,- -- | The current likelihood.- likelihood :: Log Double+ -- | The current prior value.+ prior :: Prior,+ -- | The current likelihood value.+ likelihood :: Likelihood } deriving (Eq, Ord, Show, Read)
src/Mcmc/Chain/Save.hs view
@@ -1,13 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} --- |--- Module : Mcmc.Chain.Save--- Description : Save and load a Markov chain--- Copyright : (c) Dominik Schrempf, 2020--- License : GPL-3.0-or-later---- -- Maintainer : dominik.schrempf@gmail.com -- Stability : unstable -- Portability : portable@@ -17,6 +10,12 @@ -- Save and load chains. It is easy to save and restore the current state and -- likelihood (or the trace), but it is not feasible to store all the proposals -- and so on, so they have to be provided again when continuing a run.++-- |+-- Module : Mcmc.Chain.Save+-- Description : Save and load a Markov chain+-- Copyright : (c) Dominik Schrempf, 2021+-- License : GPL-3.0-or-later module Mcmc.Chain.Save ( SavedChain (..), toSavedChain,@@ -30,6 +29,7 @@ import Data.List hiding (cycle) import qualified Data.Map as M import Data.Maybe+import qualified Data.Stack.Circular as C import qualified Data.Vector as VB import qualified Data.Vector.Unboxed as VU import Data.Word@@ -40,20 +40,18 @@ import Mcmc.Monitor import Mcmc.Proposal import Prelude hiding (cycle)-import qualified Data.Stack.Circular as C -- | Storable values of a Markov chain. -- -- See 'toSavedChain'. data SavedChain a = SavedChain- {- savedId :: Int,+ { savedId :: Maybe Int, savedLink :: Link a, savedIteration :: Int, savedTrace :: C.Stack VB.Vector (Link a), savedAcceptance :: Acceptance Int, savedSeed :: VU.Vector Word32,- savedTuningParameters :: [Maybe Double]+ savedTuningParameters :: [Maybe TuningParameter] } deriving (Eq, Read, Show) @@ -70,7 +68,7 @@ where ps = ccProposals cc ac' = transformKeysA ps [0 ..] ac- ts = [fmap tParam mt | mt <- map pTuner ps]+ ts = [fmap tParam mt | mt <- map prTuner ps] -- | Load a saved chain. --@@ -87,13 +85,19 @@ IO (Chain a) fromSavedChain pr lh cc mn (SavedChain ci it i tr ac' g' ts) | pr (state it) /= prior it =- error "fromSave: Provided prior function does not match the saved prior."+ let msg =+ unlines+ [ "fromSave: Provided prior function does not match the saved prior.",+ "fromSave: Current prior:" <> show (prior it) <> ".",+ "fromSave: Given prior:" <> show (pr $ state it) <> "."+ ]+ in error msg | lh (state it) /= likelihood it = error "fromSave: Provided likelihood function does not match the saved likelihood." | otherwise = do- g <- loadGen g'- tr' <- thawT tr- return $ Chain ci it i tr' ac g i pr lh cc' mn+ g <- loadGen g'+ tr' <- thawT tr+ return $ Chain ci it i tr' ac g i pr lh cc' mn where ac = transformKeysA [0 ..] (ccProposals cc) ac' getTuningF mt = case mt of
src/Mcmc/Chain/Trace.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Chain.Trace -- Description : History of a Markov chain--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
src/Mcmc/Environment.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Environment -- Description : Runtime environment--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -12,38 +12,75 @@ module Mcmc.Environment ( Environment (..), initializeEnvironment,+ closeEnvironment, ) where +import Control.Concurrent.MVar+import Control.Monad import Data.Time.Clock+import Mcmc.Logger import Mcmc.Settings import System.IO -- | The environment of an MCMC run.-data Environment = Environment- { settings :: Settings,- -- | We have to use 'Maybe' here, because we do not want to open any log- -- file when being 'Quiet'.- logHandle :: Maybe Handle,+data Environment s = Environment+ { settings :: s,+ -- | List will be empty if using 'Quiet'. If 'LogStdOutAndFile' is used+ -- 'logHandles' contains two handles to the standard output and the log+ -- file.+ logHandles :: [Handle],+ -- | MVar blocking output.+ outLock :: MVar (), -- | Used to calculate the ETA. startingTime :: UTCTime }- deriving (Eq, Show)+ deriving (Eq) +instance HasExecutionMode s => HasExecutionMode (Environment s) where+ getExecutionMode = getExecutionMode . settings++instance HasLock (Environment s) where+ getLock = outLock++instance HasLogHandles (Environment s) where+ getLogHandles = logHandles++instance HasStartingTime (Environment s) where+ getStartingTime = startingTime++instance HasLogMode s => HasLogMode (Environment s) where+ getLogMode = getLogMode . settings++instance HasVerbosity s => HasVerbosity (Environment s) where+ getVerbosity = getVerbosity . settings+ -- | Initialize the environment. -- -- Open log file, get current time. initializeEnvironment ::- Settings ->- IO Environment+ (HasAnalysisName s, HasExecutionMode s, HasLogMode s, HasVerbosity s) =>+ s ->+ IO (Environment s) initializeEnvironment s = do t <- getCurrentTime- mh <- case sVerbosity s of- Quiet -> return Nothing- _ -> do+ mh <- case (getLogMode s, getVerbosity s) of+ (_, Quiet) -> return []+ (LogStdOutAndFile, _) -> do h <- openWithExecutionMode em fn- return $ Just h- return $ Environment s mh t+ return [stdout, h]+ (LogFileOnly, _) -> do+ h <- openWithExecutionMode em fn+ return [h]+ (LogStdOutOnly, _) -> return [stdout]+ lock <- newMVar ()+ return $ Environment s mh lock t where- fn = fromAnalysisName (sAnalysisName s) ++ ".log"- em = sExecutionMode s+ fn = fromAnalysisName (getAnalysisName s) ++ ".mcmc.log"+ em = getExecutionMode s++-- | Close file handles.+closeEnvironment :: Environment s -> IO ()+closeEnvironment e = forM_ hs hClose+ where+ hs = filter (/= stdout) $ logHandles e
src/Mcmc/Internal/ByteString.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Internal.ByteString -- Description : ByteString tools--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
src/Mcmc/Internal/Random.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Internal.Random -- Description : Tools for random calculations--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
src/Mcmc/Internal/Shuffle.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Internal.Shuffle -- Description : Shuffle a list--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
+ src/Mcmc/Likelihood.hs view
@@ -0,0 +1,29 @@+-- |+-- Module : Mcmc.Likelihood+-- Description : Types and convenience functions for computing likelihoods+-- Copyright : (c) 2021 Dominik Schrempf+-- License : GPL-3.0-or-later+--+-- Maintainer : dominik.schrempf@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- Creation date: Wed Mar 3 11:39:04 2021.+module Mcmc.Likelihood+ ( Likelihood,+ LikelihoodFunction,+ noLikelihood,+ )+where++import Numeric.Log++-- | Likelihood values are stored in log domain.+type Likelihood = Log Double++-- | Likelihood function.+type LikelihoodFunction a = a -> Log Double++-- | Flat likelihood function. Useful for testing and debugging.+noLikelihood :: LikelihoodFunction a+noLikelihood = const 1.0
+ src/Mcmc/Logger.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module : Mcmc.Logger+-- Description : Minimal monad logger+-- Copyright : (c) Dominik Schrempf 2021+-- License : GPL-3.0-or-later+--+-- Maintainer : dominik.schrempf@gmail.com+-- Stability : unstable+-- Portability : portable+--+-- Creation date: Tue Jan 12 09:03:04 2021.+module Mcmc.Logger+ ( LogMode (..),+ Verbosity (..),+ HasLock (..),+ HasLogHandles (..),+ HasStartingTime (..),+ HasLogMode (..),+ HasVerbosity (..),+ Logger,+ logOutB,+ logDebugB,+ logDebugS,+ logWarnB,+ logWarnS,+ logInfoB,+ logInfoS,+ logInfoHeader,+ logInfoStartingTime,+ logInfoEndTime,+ )+where++import Control.Concurrent.MVar+import Control.Monad+import Control.Monad.IO.Class+import Control.Monad.Trans.Reader+import Data.Aeson+import Data.Aeson.TH+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Time.Clock+import Mcmc.Monitor.Time+import System.IO+import Paths_mcmc (version)+import Data.Version (showVersion)++-- TODO: Combine LogMode and Verbosity to:+--+-- data Verbosity = Quiet | Warn LogMode | Info LogMode | Debug LogMode++-- | Define where the log output should be directed to.+data LogMode = LogStdOutAndFile | LogStdOutOnly | LogFileOnly+ deriving (Eq, Read, Show)++$(deriveJSON defaultOptions ''LogMode)++-- | Not much to say here.+data Verbosity = Quiet | Warn | Info | Debug+ deriving (Eq, Ord, Read, Show)++$(deriveJSON defaultOptions ''Verbosity)++-- | Types with an output lock for concurrent output.+class HasLock e where+ getLock :: e -> MVar ()++-- | Types with logging information.+class HasLogHandles e where+ getLogHandles :: e -> [Handle]++-- | Types with starting time.+class HasStartingTime s where+ getStartingTime :: s -> UTCTime++-- | Types with a log mode.+class HasLogMode s where+ getLogMode :: s -> LogMode++-- | Types with verbosity.+class HasVerbosity s where+ getVerbosity :: s -> Verbosity++-- | Reader transformer used for logging to a file and to standard output.+type Logger e a = ReaderT e IO a++msgPrepare :: BL.ByteString -> BL.ByteString -> BL.ByteString+msgPrepare pref msg = BL.intercalate "\n" $ map (BL.append pref) $ BL.lines msg++-- Make sure that concurrent output is not scrambled.+atomicAction :: HasLock e => IO () -> Logger e ()+atomicAction a = do+ l <- reader getLock+ liftIO $ withMVar l (const a)++-- | Write to standard output and maybe to log file.+logOutB ::+ (HasLogHandles e, HasLock e) =>+ -- | Prefix.+ BL.ByteString ->+ -- | Message.+ BL.ByteString ->+ Logger e ()+logOutB pref msg = do+ hs <- reader getLogHandles+ mapM_ (atomicAction . (`BL.hPutStrLn` msg')) hs+ where+ msg' = msgPrepare pref msg++-- Perform debug action.+logDebugA :: (HasLock e, HasLogHandles e, HasVerbosity e) => Logger e () -> Logger e ()+logDebugA a = reader getVerbosity >>= \v -> when (v >= Debug) a++-- | Log debug message.+logDebugB :: (HasLock e, HasLogHandles e, HasVerbosity e) => BL.ByteString -> Logger e ()+logDebugB = logDebugA . logOutB "D: "++-- | Log debug message.+logDebugS :: (HasLock e, HasLogHandles e, HasVerbosity e) => String -> Logger e ()+logDebugS = logDebugB . BL.pack++-- Perform warning action.+logWarnA :: (HasLogHandles e, HasVerbosity e) => Logger e () -> Logger e ()+logWarnA a = reader getVerbosity >>= \v -> when (v >= Warn) a++-- | Log warning message.+logWarnB :: (HasLock e, HasLogHandles e, HasVerbosity e) => BL.ByteString -> Logger e ()+logWarnB = logWarnA . logOutB "W: "++-- | Log warning message.+logWarnS :: (HasLock e, HasLogHandles e, HasVerbosity e) => String -> Logger e ()+logWarnS = logWarnB . BL.pack++-- Perform info action.+logInfoA :: (HasLogHandles e, HasVerbosity e) => Logger e () -> Logger e ()+logInfoA a = reader getVerbosity >>= \v -> when (v >= Info) a++-- | Log info message.+logInfoB :: (HasLock e, HasLogHandles e, HasVerbosity e) => BL.ByteString -> Logger e ()+logInfoB = logInfoA . logOutB " "++-- | Log info message.+logInfoS :: (HasLock e, HasLogHandles e, HasVerbosity e) => String -> Logger e ()+logInfoS = logInfoB . BL.pack++-- | Log info header.+logInfoHeader :: (HasLock e, HasLogHandles e, HasVerbosity e) => Logger e ()+logInfoHeader = do+ logInfoS (replicate 70 '-')+ logInfoS ("MCMC sampler; version " ++ showVersion version <> ".")+ logInfoS "Developed by: Dominik Schrempf."+ logInfoS "License: GPL-3.0-or-later."+ logInfoS (replicate 70 '-')++-- | Log starting time.+logInfoStartingTime :: (HasLock e, HasLogHandles e, HasStartingTime e, HasVerbosity e) => Logger e ()+logInfoStartingTime = do+ ti <- reader getStartingTime+ logInfoS $ "Starting time: " <> renderTime ti++-- | Log end time.+logInfoEndTime :: (HasLock e, HasLogHandles e, HasStartingTime e, HasVerbosity e) => Logger e ()+logInfoEndTime = do+ ti <- reader getStartingTime+ te <- liftIO getCurrentTime+ let dt = te `diffUTCTime` ti+ logInfoB $ "Wall clock run time: " <> renderDuration dt <> "."+ logInfoS $ "End time: " <> renderTime te
+ src/Mcmc/MarginalLikelihood.hs view
@@ -0,0 +1,404 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module : Mcmc.MarginalLikelihood+-- Description : Calculate the marginal likelihood+-- Copyright : (c) Dominik Schrempf 2021+-- License : GPL-3.0-or-later+--+-- Maintainer : dominik.schrempf@gmail.com+-- Stability : unstable+-- Portability : portable+--+-- Creation date: Mon Jan 11 16:34:18 2021.+module Mcmc.MarginalLikelihood+ ( MarginalLikelihood,+ NPoints (..),+ MLAlgorithm (..),+ MLSettings (..),+ marginalLikelihood,+ )+where++import Control.Monad+import Control.Monad.IO.Class+import qualified Control.Monad.Parallel as P+import Control.Monad.Trans.Reader+import Data.Aeson+import Data.List hiding (cycle)+import qualified Data.Map.Strict as M+import qualified Data.Vector as VB+import qualified Data.Vector.Unboxed as VU+import Mcmc.Algorithm.MHG+import Mcmc.Chain.Chain+import Mcmc.Chain.Link+import Mcmc.Chain.Trace+import Mcmc.Environment+import Mcmc.Internal.Random+import Mcmc.Likelihood+import Mcmc.Logger+import Mcmc.Mcmc+import Mcmc.Monitor+import Mcmc.Proposal+import Mcmc.Settings+import Numeric.Log hiding (sum)+import System.Directory+import System.Random.MWC+import Text.Printf+import Text.Show.Pretty+import Prelude hiding (cycle)++-- | Marginal likelihood values are stored in log domain.+type MarginalLikelihood = Log Double++-- Reciprocal temperature value traversed along the path integral.+type Point = Double++-- | The number of points used to approximate the path integral.+newtype NPoints = NPoints {fromNPoints :: Int}+ deriving (Eq, Read, Show)++-- | Algorithms to calculate the marginal likelihood.+data MLAlgorithm+ = -- | Use a classical path integral. Also known as thermodynamic integration.+ -- In particular, /Annealing-Melting Integration/ is used.+ --+ -- See Lartillot, N., & Philippe, H., Computing Bayes Factors Using+ -- Thermodynamic Integration, Systematic Biology, 55(2), 195–207 (2006).+ -- http://dx.doi.org/10.1080/10635150500433722+ ThermodynamicIntegration+ | -- | Use stepping stone sampling.+ --+ -- See Xie, W., Lewis, P. O., Fan, Y., Kuo, L., & Chen, M., Improving+ -- marginal likelihood estimation for Bayesian phylogenetic model selection,+ -- Systematic Biology, 60(2), 150–160 (2010).+ -- http://dx.doi.org/10.1093/sysbio/syq085+ --+ -- Or Fan, Y., Wu, R., Chen, M., Kuo, L., & Lewis, P. O., Choosing among+ -- partition models in bayesian phylogenetics, Molecular Biology and+ -- Evolution, 28(1), 523–532 (2010). http://dx.doi.org/10.1093/molbev/msq224+ SteppingStoneSampling+ deriving (Eq, Read, Show)++-- | Settings of the marginal likelihood estimation.+data MLSettings = MLSettings+ { mlAnalysisName :: AnalysisName,+ mlAlgorithm :: MLAlgorithm,+ mlNPoints :: NPoints,+ -- | Initial burn in at the starting point of the path.+ mlInitialBurnIn :: BurnInSpecification,+ -- | Repetitive burn in at each point on the path.+ mlPointBurnIn :: BurnInSpecification,+ -- | The number of iterations performed at each point.+ mlIterations :: Iterations,+ mlExecutionMode :: ExecutionMode,+ mlLogMode :: LogMode,+ mlVerbosity :: Verbosity+ }+ deriving (Eq, Read, Show)++instance HasAnalysisName MLSettings where+ getAnalysisName = mlAnalysisName++instance HasExecutionMode MLSettings where+ getExecutionMode = mlExecutionMode++instance HasLogMode MLSettings where+ getLogMode = mlLogMode++instance HasVerbosity MLSettings where+ getVerbosity = mlVerbosity++type ML a = ReaderT (Environment MLSettings) IO a++-- See 'getPoints'. Alpha=0.3 is the standard choice.+alpha :: Double+alpha = 0.3++-- Distribute the points according to a skewed beta distribution with given+-- 'alpha' value. If alpha is below 1.0, more points at lower values, which is+-- desired. It is inconvenient that the reciprocal temperatures are denoted as+-- beta, and we also use the beta distribution :). Don't mix them up!+--+-- See discussion in Xie, W., Lewis, P. O., Fan, Y., Kuo, L., & Chen, M.,+-- Improving marginal likelihood estimation for bayesian phylogenetic model+-- selection, Systematic Biology, 60(2), 150–160 (2010).+-- http://dx.doi.org/10.1093/sysbio/syq085+--+-- Or Figure 1 in Höhna, S., Landis, M. J., & Huelsenbeck, J. P., Parallel power+-- posterior analyses for fast computation of marginal likelihoods in+-- phylogenetics (2017). http://dx.doi.org/10.1101/104422+getPoints :: NPoints -> [Point]+getPoints x = [f i ** (1.0 / alpha) | i <- [0 .. k1]]+ where+ k = fromNPoints x+ k1 = pred k+ f j = fromIntegral j / fromIntegral k1++sampleAtPoint ::+ ToJSON a =>+ Point ->+ Settings ->+ LikelihoodFunction a ->+ MHG a ->+ ML (MHG a)+sampleAtPoint x ss lhf a = do+ a'' <- liftIO $ mcmc ss' a'+ let ch'' = fromMHG a''+ ac = acceptance ch''+ mAr = sequence $ acceptanceRates ac+ logDebugB "sampleAtPoint: Summarize cycle."+ logDebugB $ summarizeCycle ac $ cycle ch''+ case mAr of+ Nothing -> logWarnB "Some acceptance rates are unavailable. The tuning period may be too small."+ Just ar -> do+ unless (M.null $ M.filter (<= 0.1) ar) $ logWarnB "Some acceptance rates are below 0.1."+ unless (M.null $ M.filter (>= 0.9) ar) $ logWarnB "Some acceptance rates are above 0.9."+ return a''+ where+ -- For debugging set a proper analysis name.+ nm = sAnalysisName ss+ getName :: Point -> AnalysisName+ getName y = nm <> AnalysisName ("/" <> printf "point%.8f" y)+ ss' = ss {sAnalysisName = getName x}+ -- Amend the likelihood function. Don't calculate the likelihood when the+ -- point is 0.0.+ lhf' = if x == 0.0 then const 1.0 else (** Exp (log x)) . lhf+ -- Amend the MHG algorithm.+ ch = fromMHG a+ l = link ch+ ch' =+ ch+ { -- Important: Update the likelihood using the new likelihood function.+ link = l {likelihood = lhf' $ state l},+ iteration = 0,+ start = 0,+ likelihoodFunction = lhf'+ }+ a' = MHG ch'++traversePoints ::+ ToJSON a =>+ -- Current point.+ Int ->+ NPoints ->+ [Point] ->+ Settings ->+ LikelihoodFunction a ->+ MHG a ->+ -- For each point a vector of obtained likelihoods stored in the log domain.+ ML [VU.Vector Likelihood]+traversePoints _ _ [] _ _ _ = return []+traversePoints i k (b : bs) ss lhf a = do+ logInfoS $ "Point " <> show i <> " of " <> show k' <> ": " <> show b <> "."+ a' <- sampleAtPoint b ss lhf a+ -- Get the links samples at this point.+ ls <- liftIO $ takeT n $ trace $ fromMHG a'+ -- Extract the likelihoods.+ --+ -- NOTE: This could be sped up by mapping (** -b) on the power likelihoods.+ --+ -- NOTE: This bang is an important one, because if the lhs are not strictly+ -- calculated here, the complete MCMC runs are dragged along before doing so+ -- resulting in a severe memory leak.+ let !lhs = VU.convert $ VB.map (lhf . state) ls+ -- Sample the other points.+ lhss <- traversePoints (i + 1) k bs ss lhf a'+ return $ lhs : lhss+ where+ n = fromIterations $ sIterations ss+ (NPoints k') = k++mlRun ::+ ToJSON a =>+ NPoints ->+ [Point] ->+ ExecutionMode ->+ Verbosity ->+ PriorFunction a ->+ LikelihoodFunction a ->+ Cycle a ->+ Monitor a ->+ a ->+ GenIO ->+ -- For each point a vector of likelihoods stored in log domain.+ ML [VU.Vector Likelihood]+mlRun k xs em vb prf lhf cc mn i0 g = do+ logDebugB "mlRun: Begin."+ s <- reader settings+ let nm = mlAnalysisName s+ is = mlIterations s+ biI = mlInitialBurnIn s+ biP = mlPointBurnIn s+ -- Only log sub MCMC samplers when debugging.+ vb' = case vb of+ Debug -> Debug+ _ -> Quiet+ ssI = Settings nm biI (Iterations 0) em Sequential NoSave LogFileOnly vb'+ ssP = Settings nm biP is em Sequential NoSave LogFileOnly vb'+ trLen = TraceMinimum $ fromIterations is+ logDebugB "mlRun: Initialize MHG algorithm."+ a0 <- liftIO $ mhg prf lhf cc mn trLen i0 g+ logDebugS $ "mlRun: Perform initial burn in at first point " <> show x0 <> "."+ a1 <- sampleAtPoint x0 ssI lhf a0+ logDebugB "mlRun: Traverse points."+ traversePoints 1 k xs ssP lhf a1+ where+ x0 = head xs++-- Use lists since the number of points is expected to be low.+integrateSimpsonTriangle ::+ -- X values.+ [Point] ->+ -- Y values.+ [Double] ->+ -- Integral.+ Double+integrateSimpsonTriangle xs ys = 0.5 * go xs ys+ where+ go (p0 : p1 : ps) (z0 : z1 : zs) = (z0 + z1) * (p1 - p0) + go (p1 : ps) (z1 : zs)+ go _ _ = 0++tiWrapper ::+ ToJSON a =>+ MLSettings ->+ PriorFunction a ->+ LikelihoodFunction a ->+ Cycle a ->+ Monitor a ->+ a ->+ GenIO ->+ ML MarginalLikelihood+tiWrapper s prf lhf cc mn i0 g = do+ logInfoB "Path integral (thermodynamic integration)."+ [g0, g1] <- splitGen 2 g++ -- Parallel execution of both path integrals.+ [lhssForward, lhssBackward] <-+ P.sequence+ [ mlRun k bsForward em vb prf lhf cc mn i0 g0,+ mlRun k bsBackward em vb prf lhf cc mn i0 g1+ ]+ logInfoEndTime++ logDebugB "tiWrapper: Calculate mean log likelihoods."+ -- It is important to average across the log likelihoods here (and not the+ -- likelihoods). I am not exactly sure why this is.+ let getMeanLogLhs = map (\x -> VU.sum (VU.map ln x) / fromIntegral (VU.length x))+ mlForward = integrateSimpsonTriangle bsForward (getMeanLogLhs lhssForward)+ mlBackward = negate $ integrateSimpsonTriangle bsBackward (getMeanLogLhs lhssBackward)+ logDebugS $ "tiWrapper: Marginal log likelihood of forward integral: " ++ show mlForward+ logDebugS $ "tiWrapper: Marginal log likelihood of backward integral: " ++ show mlBackward+ let mean = 0.5 * (mlForward + mlBackward)+ logDebugS $ "tiWrapper: The mean is: " ++ show mean+ return $ Exp mean+ where+ k = mlNPoints s+ bsForward = getPoints k+ bsBackward = reverse bsForward+ em = mlExecutionMode s+ vb = mlVerbosity s++-- Helper function to exponentiate log domain values with a double value.+pow' :: Log Double -> Double -> Log Double+pow' x p = Exp $ ln x * p++-- See Xie2010 p. 153, bottom left.+sssCalculateMarginalLikelihood :: [Point] -> [VU.Vector Likelihood] -> MarginalLikelihood+sssCalculateMarginalLikelihood xs lhss = product $ zipWith3 f xs (tail xs) lhss+ where+ f :: Point -> Point -> VU.Vector Likelihood -> MarginalLikelihood+ -- f beta_{k-1} beta_k lhs_{k-1}+ f bkm1 bk lhs = n1 * VU.sum lhsPowered+ where+ n1 = recip $ fromIntegral $ VU.length lhs+ dbeta = bk - bkm1+ lhsPowered = VU.map (`pow'` dbeta) lhs++-- -- Numerical stability by factoring out lhMax. But no observed+-- -- improvement towards the standard version.+--+-- f bkm1 bk lhs = n1 * pow' lhMax dbeta * VU.sum lhsNormedPowered+-- where n1 = recip $ fromIntegral $ VU.length lhs+-- lhMax = VU.maximum lhs+-- dbeta = bk - bkm1+-- lhsNormed = VU.map (/lhMax) lhs+-- lhsNormedPowered = VU.map (`pow'` dbeta) lhsNormed++-- -- Computation of the log of the marginal likelihood. According to the paper,+-- -- this estimator is biased and I did not observe any improvements compared+-- -- to the direct estimator implemented above.+--+-- -- See Xie2010 p. 153, top right.+-- sssCalculateMarginalLikelihood' :: [Point] -> [VU.Vector Likelihood] -> MarginalLikelihood+-- sssCalculateMarginalLikelihood' xs lhss = Exp $ sum $ zipWith3 f xs (tail xs) lhss+-- where f :: Point -> Point -> VU.Vector Likelihood -> Double+-- -- f beta_{k-1} beta_k lhs_{k-1}+-- f bkm1 bk lhs = dbeta * llhMax + log (n1 * VU.sum lhsNormedPowered)+-- where dbeta = bk - bkm1+-- llhMax = ln $ VU.maximum lhs+-- n1 = recip $ fromIntegral $ VU.length lhs+-- llhs = VU.map ln lhs+-- llhsNormed = VU.map (\x -> x - llhMax) llhs+-- lhsNormedPowered = VU.map (\x -> exp $ dbeta * x) llhsNormed+sssWrapper ::+ ToJSON a =>+ MLSettings ->+ PriorFunction a ->+ LikelihoodFunction a ->+ Cycle a ->+ Monitor a ->+ a ->+ GenIO ->+ ML MarginalLikelihood+sssWrapper s prf lhf cc mn i0 g = do+ logInfoB "Stepping stone sampling."+ logLhss <- mlRun k bsForward' em vb prf lhf cc mn i0 g+ logInfoB "The last point does not need to be sampled with stepping stone sampling."+ logDebugB "sssWrapper: Calculate marginal likelihood."+ return $ sssCalculateMarginalLikelihood bsForward logLhss+ where+ k = mlNPoints s+ bsForward = getPoints k+ bsForward' = init bsForward+ em = mlExecutionMode s+ vb = mlVerbosity s++-- | Estimate the marginal likelihood.+marginalLikelihood ::+ ToJSON a =>+ MLSettings ->+ PriorFunction a ->+ LikelihoodFunction a ->+ Cycle a ->+ Monitor a ->+ InitialState a ->+ -- | A source of randomness. For reproducible runs, make sure to use+ -- generators with the same seed.+ GenIO ->+ IO MarginalLikelihood+marginalLikelihood s prf lhf cc mn i0 g = do+ -- Initialize.+ e <- initializeEnvironment s++ when (mlVerbosity s == Debug) $ do+ let n = fromAnalysisName $ mlAnalysisName s+ createDirectoryIfMissing True n++ -- Run.+ runReaderT+ ( do+ logInfoStartingTime+ logInfoB "Estimate marginal likelihood."+ logDebugB "marginalLikelihood: The marginal likelihood settings are:"+ logDebugS $ ppShow s+ val <- case mlAlgorithm s of+ ThermodynamicIntegration -> tiWrapper s prf lhf cc mn i0 g+ SteppingStoneSampling -> sssWrapper s prf lhf cc mn i0 g+ logInfoS $ "Marginal log likelihood: " ++ show (ln val)+ -- TODO: Simulation variance.+ logInfoS "The simulation variance is not yet available."+ return val+ )+ e
src/Mcmc/Mcmc.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Mcmc -- Description : Framework for running Markov chain Monte Carlo samplers--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -23,14 +23,10 @@ import Control.Monad import Control.Monad.IO.Class--- import Control.Monad.Trans.RWS.CPS import Control.Monad.Trans.Reader-import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Maybe-import Data.Time.Clock import Mcmc.Algorithm import Mcmc.Environment-import Mcmc.Monitor.Time+import Mcmc.Logger import Mcmc.Settings import System.IO import Text.Show.Pretty@@ -38,79 +34,24 @@ -- The MCMC algorithm has read access to an environment and uses an algorithm -- transforming the state @a@.-type MCMC a = ReaderT Environment IO a--msgPrepare :: BL.ByteString -> BL.ByteString -> BL.ByteString-msgPrepare pref msg = BL.intercalate "\n" $ map (BL.append pref) $ BL.lines msg---- Write to standard output and log file.-mcmcOutB :: BL.ByteString -> BL.ByteString -> MCMC ()-mcmcOutB pref msg = do- h <- fromMaybe (error "mcmcOut: Log handle is missing.") <$> reader logHandle- liftIO $ BL.putStrLn msg' >> BL.hPutStrLn h msg'- where- msg' = msgPrepare pref msg---- -- Perform warning action.--- mcmcWarnA :: MCMC a () -> MCMC a ()--- mcmcWarnA a = reader (verbosity . settings) >>= \v -> when (v >= Warn) a---- -- Print warning message.--- mcmcWarnB :: BL.ByteString -> MCMC a ()--- mcmcWarnB = mcmcWarnA . mcmcOutB . msgPrepare 'W'---- -- Print warning message.--- mcmcWarnS :: String -> MCMC a ()--- mcmcWarnS = mcmcWarnB . BL.pack---- Perform info action.-mcmcInfoA :: MCMC () -> MCMC ()-mcmcInfoA a = reader (sVerbosity . settings) >>= \v -> when (v >= Info) a---- Print info message.-mcmcInfoB :: BL.ByteString -> MCMC ()-mcmcInfoB = mcmcInfoA . mcmcOutB "I: "---- Print info message.-mcmcInfoS :: String -> MCMC ()-mcmcInfoS = mcmcInfoB . BL.pack---- Perform debug action.-mcmcDebugA :: MCMC () -> MCMC ()-mcmcDebugA a = reader (sVerbosity . settings) >>= \v -> when (v == Debug) a---- Print debug message.-mcmcDebugB :: BL.ByteString -> MCMC ()-mcmcDebugB = mcmcDebugA . mcmcOutB "D: "---- Print debug message.-mcmcDebugS :: String -> MCMC ()-mcmcDebugS = mcmcDebugB . BL.pack--mcmcReportTime :: MCMC ()-mcmcReportTime = do- mcmcDebugB "Report time."- ti <- reader startingTime- mcmcInfoS $ "Starting time of MCMC sampler: " <> renderTime ti+type MCMC a = ReaderT (Environment Settings) IO a mcmcExecute :: Algorithm a => a -> MCMC a mcmcExecute a = do- mcmcDebugB "Executing MCMC run."+ logDebugB "Executing MCMC run." s <- reader settings a' <- case sExecutionMode s of Fail -> mcmcNewRun a Overwrite -> mcmcNewRun a Continue -> mcmcContinueRun a- mcmcDebugB "Executed MCMC run."+ logDebugB "Executed MCMC run." return a' --- Reset acceptance counts. mcmcResetAcceptance :: Algorithm a => a -> MCMC a mcmcResetAcceptance a = do- mcmcDebugB "Reset acceptance rates."+ logDebugB "Reset acceptance rates." return $ aResetAcceptance a --- Execute the monitors of the chain. mcmcExecuteMonitors :: Algorithm a => a -> MCMC () mcmcExecuteMonitors a = do e <- ask@@ -119,7 +60,7 @@ t0 = startingTime e iTotal = burnInIterations (sBurnIn s) + fromIterations (sIterations s) mStdLog <- liftIO (aExecuteMonitors vb t0 iTotal a)- forM_ mStdLog (mcmcOutB " ")+ forM_ mStdLog (logOutB " ") mcmcIterate :: Algorithm a => Int -> a -> MCMC a mcmcIterate n a@@ -134,30 +75,30 @@ mcmcNewRun :: Algorithm a => a -> MCMC a mcmcNewRun a = do s <- reader settings- mcmcInfoB "Start new MCMC sampler."- mcmcInfoB "Initial state."- mcmcInfoB $ aStdMonitorHeader a+ logInfoB "Start new MCMC sampler."+ logInfoB "Initial state."+ logInfoB $ aStdMonitorHeader a mcmcExecuteMonitors a- mcmcInfoB $ aSummarizeCycle a+ when (aIsInValidState a) (logWarnB "The initial state is invalid!")+ logInfoB $ aSummarizeCycle a a' <- mcmcBurnIn a- a'' <- mcmcResetAcceptance a' let i = fromIterations $ sIterations s- mcmcInfoS $ "Run chain for " ++ show i ++ " iterations."- mcmcInfoB $ aStdMonitorHeader a''- mcmcIterate i a''+ logInfoS $ "Run chain for " ++ show i ++ " iterations."+ logInfoB $ aStdMonitorHeader a'+ mcmcIterate i a' mcmcContinueRun :: Algorithm a => a -> MCMC a mcmcContinueRun a = do s <- reader settings let iTotal = fromIterations (sIterations s) + burnInIterations (sBurnIn s)- mcmcInfoB "Continuation of MCMC sampler."+ logInfoB "Continuation of MCMC sampler." let iCurrent = aIteration a- mcmcInfoS $ "Current iteration: " ++ show iCurrent ++ "."- mcmcInfoS $ "Total iterations: " ++ show iTotal ++ "."+ logInfoS $ "Current iteration: " ++ show iCurrent ++ "."+ logInfoS $ "Total iterations: " ++ show iTotal ++ "." let di = iTotal - iCurrent- mcmcInfoB $ aSummarizeCycle a- mcmcInfoS $ "Run chain for " ++ show di ++ " iterations."- mcmcInfoB $ aStdMonitorHeader a+ logInfoB $ aSummarizeCycle a+ logInfoS $ "Run chain for " ++ show di ++ " iterations."+ logInfoB $ aStdMonitorHeader a mcmcIterate di a mcmcBurnIn :: Algorithm a => a -> MCMC a@@ -165,53 +106,67 @@ s <- reader settings case sBurnIn s of NoBurnIn -> do- mcmcInfoS "No burn in."+ logInfoS "No burn in." return a BurnInWithoutAutoTuning n -> do- mcmcInfoS $ "Burn in for " <> show n <> " iterations."- mcmcInfoS "Auto tuning is disabled."- mcmcInfoB $ aStdMonitorHeader a+ logInfoS $ "Burn in for " <> show n <> " iterations."+ logInfoS "Auto tuning is disabled."+ logInfoB $ aStdMonitorHeader a a' <- mcmcIterate n a- mcmcInfoB $ aSummarizeCycle a'- mcmcInfoB "Burn in finished."- return a'+ logInfoB $ aSummarizeCycle a'+ a'' <- mcmcResetAcceptance a'+ logInfoB "Burn in finished."+ return a'' BurnInWithAutoTuning n t -> do- mcmcInfoS $ "Burn in for " ++ show n ++ " iterations."- mcmcInfoS $ "Auto tuning is enabled with a period of " ++ show t ++ "."- mcmcInfoB $ aStdMonitorHeader a- a' <- mcmcBurnInWithAutoTuning n t a- mcmcInfoB "Burn in finished."+ logInfoS $ "Burn in for " ++ show n ++ " iterations."+ logInfoS $ "Auto tuning is enabled with a period of " ++ show t ++ "."+ logInfoB $ aStdMonitorHeader a+ let (m, r) = n `divMod` t+ -- Don't add if 0. Because then we auto tune without acceptance counts+ -- and get NaNs.+ xs = replicate m t <> [r | r > 0]+ a' <- mcmcBurnInWithAutoTuning xs a+ logInfoB "Burn in finished." return a'+ BurnInWithCustomAutoTuning xs -> do+ logInfoS $ "Burn in for " ++ show (sum xs) ++ " iterations."+ logInfoS $ "Custom auto tuning is enabled with periods " ++ show xs ++ "."+ logInfoB $ aStdMonitorHeader a+ a' <- mcmcBurnInWithAutoTuning xs a+ logInfoB "Burn in finished."+ return a' -- Auto tune the proposals. mcmcAutotune :: Algorithm a => a -> MCMC a mcmcAutotune a = do- mcmcDebugB "Auto tune."+ logDebugB "Auto tune." return $ aAutoTune a -mcmcBurnInWithAutoTuning :: Algorithm a => Int -> Int -> a -> MCMC a-mcmcBurnInWithAutoTuning b t a- | b > t = do- a' <- mcmcResetAcceptance a- a'' <- mcmcIterate t a'- mcmcDebugB $ aSummarizeCycle a''- a''' <- mcmcAutotune a''- mcmcDebugB $ aStdMonitorHeader a''- mcmcBurnInWithAutoTuning (b - t) t a'''- | otherwise = do- a' <- mcmcResetAcceptance a- a'' <- mcmcIterate b a'- mcmcInfoB $ aSummarizeCycle a''- mcmcInfoS $ "Acceptance rates calculated over the last " <> show b <> " iterations."- return a''+mcmcBurnInWithAutoTuning :: Algorithm a => [Int] -> a -> MCMC a+mcmcBurnInWithAutoTuning [] _ = error "mcmcBurnInWithAutoTuning: Empty lisst."+mcmcBurnInWithAutoTuning [x] a = do+ -- Last round.+ a' <- mcmcIterate x a+ a'' <- mcmcAutotune a'+ logInfoB $ aSummarizeCycle a''+ logInfoS $ "Acceptance rates calculated over the last " <> show x <> " iterations."+ mcmcResetAcceptance a''+mcmcBurnInWithAutoTuning (x:xs) a = do+ a' <- mcmcIterate x a+ a'' <- mcmcAutotune a'+ logDebugB $ aSummarizeCycle a''+ logDebugS $ "Acceptance rates calculated over the last " <> show x <> " iterations."+ logDebugB $ aStdMonitorHeader a''+ a''' <- mcmcResetAcceptance a''+ mcmcBurnInWithAutoTuning xs a''' mcmcInitialize :: Algorithm a => a -> MCMC a mcmcInitialize a = do- mcmcInfoS $ aName a ++ " algorithm."+ logInfoS $ aName a ++ " algorithm." s <- settings <$> ask- mcmcDebugB "Opening monitors."+ logDebugB "Opening monitors." a' <- liftIO $ aOpenMonitors (sAnalysisName s) (sExecutionMode s) a- mcmcDebugB "Monitors opened."+ logDebugB "Monitors opened." return a' -- Save the MCMC run.@@ -219,42 +174,41 @@ mcmcSave a = do s <- reader settings case sSaveMode s of- NoSave -> mcmcInfoB "Do not save the MCMC analysis."+ NoSave -> logInfoB "Do not save the MCMC analysis." Save -> do- mcmcInfoB "Save settings."+ logInfoB "Save settings." liftIO $ settingsSave s let nm = sAnalysisName s- mcmcInfoB "Save compressed MCMC analysis."- mcmcInfoB "For long traces, or complex objects, this may take a while."+ logInfoB "Save compressed MCMC analysis."+ logInfoB "For long traces, or complex objects, this may take a while." liftIO $ aSave nm a- mcmcInfoB "Markov chain saved."+ logInfoB "Markov chain saved." -- Report and finish up. mcmcClose :: Algorithm a => a -> MCMC a mcmcClose a = do- mcmcDebugB "Closing MCMC run."- mcmcInfoB $ aSummarizeCycle a- mcmcInfoS $ aName a ++ " algorithm finished."+ logDebugB "Closing MCMC run."+ logInfoB $ aSummarizeCycle a+ logInfoS $ aName a ++ " algorithm finished." mcmcSave a- ti <- reader startingTime- te <- liftIO getCurrentTime- let dt = te `diffUTCTime` ti- mcmcInfoB $ "Wall clock run time: " <> renderDuration dt <> "."- mcmcInfoS $ "End time: " <> renderTime te+ logInfoEndTime a' <- liftIO $ aCloseMonitors a- h <- reader logHandle- liftIO $ forM_ h hClose+ e <- ask+ liftIO $ closeEnvironment e return a' -- Initialize the run, execute the run, and close the run. mcmcRun :: Algorithm a => a -> MCMC a mcmcRun a = do- mcmcDebugB "The settings are:"- reader settings >>= mcmcDebugS . ppShow+ logInfoHeader + -- Debug settings.+ logDebugB "The MCMC settings are:"+ reader settings >>= logDebugS . ppShow+ -- Initialize. a' <- mcmcInitialize a- mcmcReportTime+ logInfoStartingTime -- Execute. a'' <- mcmcExecute a'@@ -274,15 +228,15 @@ -- Currently, it is only possible to continue MCMC algorithms that have -- completed successfully. This restriction is necessary, because for parallel -- chains, it is hardly possible to ensure all chains are synchronized when the--- process is killed.+-- process is killed or fails. -- -- See: ----- - 'Mcmc.Algorithm.Metropolis.mhgLoad'+-- - 'Mcmc.Algorithm.MHG.mhgLoad' -- -- - 'Mcmc.Algorithm.MC3.mc3Load'-mcmcContinue :: Algorithm a => Int -> Settings -> a -> IO a+mcmcContinue :: Algorithm a => Iterations -> Settings -> a -> IO a mcmcContinue dn s = mcmc s' where- n' = Iterations $ fromIterations (sIterations s) + dn+ n' = Iterations $ fromIterations (sIterations s) + fromIterations dn s' = s {sIterations = n', sExecutionMode = Continue}
src/Mcmc/Monitor.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Monitor -- Description : Monitor a Markov chain--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -14,11 +14,14 @@ module Mcmc.Monitor ( -- * Create monitors Monitor (..),+ Period,+ simpleMonitor, MonitorStdOut, monitorStdOut, msHeader, MonitorFile, monitorFile,+ BatchSize, MonitorBatch, monitorBatch, getMonitorBatchSize,@@ -64,21 +67,31 @@ mBatches :: [MonitorBatch a] } +-- | Monitor period.+type Period = Int++-- | Do not monitor parameters.+--+-- Monitor prior and likelihood with given period.+simpleMonitor :: Period -> Monitor a+simpleMonitor p+ | p < 1 = error "simpleMonitor: Monitor period must be 1 or larger."+ | otherwise =+ Monitor (MonitorStdOut [] p) [] []+ -- | Monitor to standard output; constructed with 'monitorStdOut'. data MonitorStdOut a = MonitorStdOut { msParams :: [MonitorParameter a],- msPeriod :: Int+ msPeriod :: Period } -- | Monitor to standard output. monitorStdOut ::- -- | Instructions about which parameters to log. [MonitorParameter a] ->- -- | Logging period.- Int ->+ Period -> MonitorStdOut a monitorStdOut ps p- | p < 1 = error "monitorStdOut: Monitor period has to be 1 or larger."+ | p < 1 = error "monitorStdOut: Monitor period must be 1 or larger." | otherwise = MonitorStdOut ps p msIWidth :: Int@@ -151,20 +164,18 @@ { mfName :: String, mfHandle :: Maybe Handle, mfParams :: [MonitorParameter a],- mfPeriod :: Int+ mfPeriod :: Period } -- | Monitor parameters to a file. monitorFile :: -- | Name; used as part of the file name. String ->- -- | Instructions about which parameters to log. [MonitorParameter a] ->- -- | Logging period.- Int ->+ Period -> MonitorFile a monitorFile n ps p- | p < 1 = error "monitorFile: Monitor period has to be 1 or larger."+ | p < 1 = error "monitorFile: Monitor period must be 1 or larger." | otherwise = MonitorFile n Nothing ps p mfRenderRow :: [BL.ByteString] -> BL.ByteString@@ -216,6 +227,9 @@ Just h -> hClose h Nothing -> error $ "mfClose: File was not opened for monitor " <> mfName m <> "." +-- | Batch size.+type BatchSize = Int+ -- | Batch monitor to a file. -- -- Calculate summary statistics over the last given number of iterations (batch@@ -224,26 +238,24 @@ { mbName :: String, mbHandle :: Maybe Handle, mbParams :: [MonitorParameterBatch a],- mbSize :: Int+ mbSize :: BatchSize } -- | Batch monitor parameters to a file, see 'MonitorBatch'. monitorBatch :: -- | Name; used as part of the file name. String ->- -- | Instructions about how to calculate the summary statistics. [MonitorParameterBatch a] ->- -- | Batch size.- Int ->+ BatchSize -> MonitorBatch a-monitorBatch n ps p- | p < 2 = error "monitorBatch: Batch size has to be 2 or larger."- | otherwise = MonitorBatch n Nothing ps p+monitorBatch n ps b+ | b < 2 = error "monitorBatch: Batch size must be 2 or larger."+ | otherwise = MonitorBatch n Nothing ps b -- | Batch monitor size. -- -- Useful to determine the trace length.-getMonitorBatchSize :: MonitorBatch a -> Int+getMonitorBatchSize :: MonitorBatch a -> BatchSize getMonitorBatchSize = mbSize mbOpen :: String -> String -> ExecutionMode -> MonitorBatch a -> IO (MonitorBatch a)@@ -322,7 +334,6 @@ -- | Execute monitors; print status information to files and return text to be -- printed to standard output and log file. mExec ::- -- | Verbosity Verbosity -> -- | Iteration. Int ->@@ -330,11 +341,9 @@ Int -> -- | Starting time. UTCTime ->- -- | Trace of Markov chain. Trace a -> -- | Total number of iterations; to calculate ETA. Int ->- -- | The monitor. Monitor a -> IO (Maybe BL.ByteString) mExec v i ss st xs j (Monitor s fs bs) = do
src/Mcmc/Monitor/Log.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Monitor.Log -- Description : Monitor logarithmic values--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
src/Mcmc/Monitor/Parameter.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Monitor.Parameter -- Description : Monitor parameters--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
src/Mcmc/Monitor/ParameterBatch.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Monitor.ParameterBatch -- Description : Batch monitor parameters--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
src/Mcmc/Monitor/Time.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Monitor.Time -- Description : Print time related values--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com
+ src/Mcmc/Posterior.hs view
@@ -0,0 +1,20 @@+-- |+-- Module : Mcmc.Posterior+-- Description : Types for posterior values and functions+-- Copyright : (c) 2021 Dominik Schrempf+-- License : GPL-3.0-or-later+--+-- Maintainer : dominik.schrempf@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- Creation date: Fri May 28 12:26:35 2021.+module Mcmc.Posterior+ ( Posterior,+ )+where++import Numeric.Log++-- | Posterior values are stored in log domain.+type Posterior = Log Double
src/Mcmc/Prior.hs view
@@ -2,8 +2,8 @@ -- | -- Module : Prior--- Description : Convenience functions for computing priors--- Copyright : (c) Dominik Schrempf, 2020+-- Description : Types and convenience functions for computing priors+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -12,7 +12,11 @@ -- -- Creation date: Thu Jul 23 13:26:14 2020. module Mcmc.Prior- ( -- * Improper priors+ ( Prior,+ PriorFunction,+ noPrior,++ -- * Improper priors largerThan, positive, lowerThan,@@ -21,6 +25,10 @@ -- * Continuous priors exponential, gamma,+ gammaMeanVariance,+ gammaMeanOne,+ gammaShapeScaleToMeanVariance,+ gammaMeanVarianceToShapeScale, normal, uniform, @@ -34,6 +42,7 @@ import Control.Monad import Data.Maybe (fromMaybe)+import Mcmc.Statistics.Types import Numeric.Log import qualified Statistics.Distribution as S import qualified Statistics.Distribution.Exponential as S@@ -41,80 +50,98 @@ import qualified Statistics.Distribution.Normal as S import qualified Statistics.Distribution.Poisson as S +-- | Prior values are stored in log domain.+type Prior = Log Double++-- | Prior function.+type PriorFunction a = a -> Prior++-- | Flat prior function. Useful for testing and debugging.+noPrior :: PriorFunction a+noPrior = const 1.0+ -- | Improper uniform prior; strictly larger than a given value.-largerThan :: Double -> Double -> Log Double+largerThan :: LowerBoundary -> PriorFunction Double largerThan a x | x <= a = 0 | otherwise = 1 -- | Improper uniform prior; strictly larger than zero.-positive :: Double -> Log Double+positive :: PriorFunction Double positive = largerThan 0 -- | Improper uniform prior; strictly lower than a given value.-lowerThan :: Double -> Double -> Log Double+lowerThan :: UpperBoundary -> PriorFunction Double lowerThan b x | x >= b = 0 | otherwise = 1 -- | Improper uniform prior; strictly lower than zero.-negative :: Double -> Log Double+negative :: PriorFunction Double negative = lowerThan 0 -- | Exponential distributed prior.-exponential ::- -- | Rate.- Double ->- Double ->- Log Double-exponential l x = Exp $ S.logDensity d x+exponential :: Rate -> PriorFunction Double+exponential l = Exp . S.logDensity d 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+gamma :: Shape -> Scale -> PriorFunction Double+gamma k t = Exp . S.logDensity d where d = S.gammaDistr k t +-- | See 'gamma' but parametrized using mean and variance.+gammaMeanVariance :: Mean -> Variance -> PriorFunction Double+gammaMeanVariance m v = Exp . S.logDensity d+ where (k, th) = gammaMeanVarianceToShapeScale m v+ d = S.gammaDistr k th++-- | Gamma disstributed prior with given shape and mean 1.0.+gammaMeanOne :: Shape -> PriorFunction Double+gammaMeanOne k = Exp . S.logDensity d+ where+ d = S.gammaDistr k (recip k)++-- The mean and variance of the gamma distribution are+--+-- m = k*t+--+-- v = k*t*t+--+-- Hence, the shape and scale are+--+-- k = m^2/v+--+-- t = v/m++-- | Calculate mean and variance of the gamma distribution given the shape and+-- the scale.+gammaShapeScaleToMeanVariance :: Shape -> Scale -> (Mean, Variance)+gammaShapeScaleToMeanVariance k t = let m = k * t in (m, m * t)++-- | Calculate shape and scale of the gamma distribution given the mean and+-- the variance.+gammaMeanVarianceToShapeScale :: Mean -> Variance -> (Shape, Scale)+gammaMeanVarianceToShapeScale m v = (m * m / v, v / m)+ -- | Normal distributed prior.-normal ::- -- | Mean.- Double ->- -- | Standard deviation.- Double ->- Double ->- Log Double-normal m s x = Exp $ S.logDensity d x+normal :: Mean -> StandardDeviation -> PriorFunction Double+normal m s = Exp . S.logDensity d where d = S.normalDistr m s -- | Uniform prior on [a, b].-uniform ::- -- | Lower bound a.- Double ->- -- | Upper bound b.- Double ->- Double ->- Log Double+uniform :: LowerBoundary -> UpperBoundary -> PriorFunction Double uniform a b x | x <= a = 0 | x >= b = 0 | otherwise = Exp 0 -- | Poisson distributed prior.-poisson ::- -- | Rate.- Double ->- Int ->- Log Double-poisson l x = Exp $ S.logProbability d x+poisson :: Rate -> PriorFunction Int+poisson l = Exp . S.logProbability d where d = S.poisson l
src/Mcmc/Proposal.hs view
@@ -6,7 +6,7 @@ -- | -- Module : Mcmc.Proposal -- Description : Proposals are instruction to move around the state space--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -15,34 +15,39 @@ -- -- Creation date: Wed May 20 13:42:53 2020. module Mcmc.Proposal- ( -- * Proposal+ ( -- * Proposals and types PName (..), PDescription (..),- PWeight (..),+ PWeight (fromPWeight),+ pWeight, PDimension (..), Proposal (..),+ KernelRatio,+ Jacobian,+ JacobianFunction, (@~),+ liftProposal,+ liftProposalWith, ProposalSimple,- Tuner (tParam, tFunc),+ Tuner (..), Tune (..), createProposal,+ TuningParameter,+ tuningParameterMin,+ tuningParameterMax, tune, getOptimalRate,- proposalHeader,- proposalHLine,- summarizeProposal, - -- * Cycle+ -- * Cycles Order (..), Cycle (ccProposals), cycleFromList, setOrder,- orderProposals,+ prepareProposals, tuneCycle, autoTuneCycle,- summarizeCycle, - -- * Acceptance+ -- * Acceptance rates Acceptance (fromAcceptance), emptyA, pushA,@@ -50,6 +55,12 @@ transformKeysA, acceptanceRate, acceptanceRates,++ -- * Output+ proposalHeader,+ proposalHLine,+ summarizeProposal,+ summarizeCycle, ) where @@ -79,11 +90,16 @@ newtype PDescription = PDescription {fromPDescription :: String} deriving (Show, Eq, Ord) --- | The weight determines how often a 'Proposal' is executed per iteration of--- the Markov chain.+-- | The positive weight determines how often a 'Proposal' is executed per+-- iteration of the Markov chain. newtype PWeight = PWeight {fromPWeight :: Int} deriving (Show, Eq, Ord) +-- | Check if the weight is positive.+pWeight :: Int -> PWeight+pWeight n | n <= 0 = error "pWeight: Proposal weight is zero or negative."+ | otherwise = PWeight n+ -- | Proposal dimension. -- -- The number of affected, independent parameters.@@ -127,48 +143,90 @@ -- -- A 'Proposal' may be tuneable in that it contains information about how to enlarge -- or shrink the step size to tune the acceptance rate.+--+-- Predefined proposals are provided. To create custom proposals, one may use+-- the convenience function 'createProposal'. data Proposal a = Proposal { -- | Name of the affected variable.- pName :: PName,+ prName :: PName, -- | Description of the proposal type and parameters.- pDescription :: PDescription,+ prDescription :: PDescription, -- | Dimension of the proposal. The dimension is used to calculate the -- optimal acceptance rate, and does not have to be exact.- pDimension :: PDimension,+ prDimension :: PDimension, -- | The weight determines how often a 'Proposal' is executed per iteration of -- the Markov chain.- pWeight :: PWeight,+ prWeight :: PWeight, -- | Simple proposal without name, weight, and tuning information.- pSimple :: ProposalSimple a,+ prSimple :: ProposalSimple a, -- | Tuning is disabled if set to 'Nothing'.- pTuner :: Maybe (Tuner a)+ prTuner :: Maybe (Tuner a) } instance Eq (Proposal a) where- m == n = pName m == pName n && pDescription m == pDescription n+ m == n = prName m == prName n && prDescription m == prDescription n instance Ord (Proposal a) where- compare = compare `on` (\p -> (pDescription p, pName p, pWeight p))+ compare = compare `on` (\p -> (prDescription p, prName p, prWeight p)) --- | Convert a proposal from one data type to another using a lens.+-- | Ratio of the proposal kernels. --+-- Part of the MHG acceptance ratio.+--+-- See also 'Jacobian'.+--+-- NOTE: Actually the 'Jacobian' should be part of the 'KernelRatio'. However,+-- it is more declarative to have them separate. It is a constant reminder: Is+-- the Jacobian modifier different from 1.0?+type KernelRatio = Log Double++-- | Absolute value of the determinant of the Jacobian matrix.+--+-- Part of the MHG acceptance ratio.+--+-- See also 'Jacobian'.+type Jacobian = Log Double++-- | Function calculating the 'Jacobian' of a proposal.+type JacobianFunction a = a -> Jacobian++-- | Lift a proposal from one data type to another.+--+-- Assume the Jacobian is 1.0 (see also 'liftProposal' and 'liftProposalWith').+-- -- For example: -- -- @ -- scaleFirstEntryOfTuple = _1 @~ scale -- @+infixl 7 @~ (@~) :: Lens' b a -> Proposal a -> Proposal b-(@~) l (Proposal n r d w s t) = Proposal n r d w (convertProposalSimple l s) (convertTuner l <$> t)+(@~) = liftProposal +-- | Lift a proposal from one data type to another.+--+-- Assume the Jacobian is 1.0 (see also '(@~)' and 'liftProposalWith').+liftProposal :: Lens' b a -> Proposal a -> Proposal b+liftProposal = liftProposalWith (const 1.0)++-- | Lift a proposal from one data type to another.+--+-- A function to calculate the Jacobian has to be provided (see also+-- 'liftProposal').+--+-- For further reference, please see the [example+-- @Pair@](https://github.com/dschrempf/mcmc/blob/master/mcmc-examples/Pair/Pair.hs).+liftProposalWith :: JacobianFunction b -> Lens' b a -> Proposal a -> Proposal b+liftProposalWith jf l (Proposal n r d w s t) = Proposal n r d w (convertProposalSimple jf l s) (convertTuner jf l <$> t)+ -- | Simple proposal without tuning information. -- -- Instruction about randomly moving from the current state to a new state, -- given some source of randomness. -- -- In order to calculate the Metropolis-Hastings-Green ratio, we need to know--- the ratio of the backward to forward kernels (i.e., the probability masses or--- probability densities) and the absolute value of the determinant of the--- Jacobian matrix.+-- the ratio of the backward to forward kernels (the 'KernelRatio' or the+-- probability masses or probability densities) and the 'Jacobian'. -- -- For unbiased proposals, these values are 1.0 such that --@@ -179,38 +237,45 @@ -- For biased proposals, the kernel ratio is qYX / qXY, where qXY is the -- probability density to move from X to Y, and the absolute value of the -- determinant of the Jacobian matrix differs from 1.0.-type ProposalSimple a = a -> GenIO -> IO (a, Log Double, Log Double)+type ProposalSimple a = a -> GenIO -> IO (a, KernelRatio, Jacobian) -convertProposalSimple :: Lens' b a -> ProposalSimple a -> ProposalSimple b-convertProposalSimple l s = s'+convertProposalSimple :: JacobianFunction b -> Lens' b a -> ProposalSimple a -> ProposalSimple b+convertProposalSimple jf l s = s' where- s' v g = do- (x', r, j) <- s (v ^. l) g- return (set l x' v, r, j)+ s' y g = do+ (x', r, j) <- s (y ^. l) g+ let y' = set l x' y+ jxy = jf y+ jyx = jf y'+ j' = j * jyx / jxy+ return (y', r, j') -- | Tune the acceptance rate of a 'Proposal'; see 'tune', or 'autoTuneCycle'. data Tuner a = Tuner- { tParam :: Double,- tFunc :: Double -> ProposalSimple a+ { tParam :: TuningParameter,+ tFunc :: TuningParameter -> ProposalSimple a } -convertTuner :: Lens' b a -> Tuner a -> Tuner b-convertTuner l (Tuner p f) = Tuner p f'+convertTuner :: JacobianFunction b -> Lens' b a -> Tuner a -> Tuner b+convertTuner jf l (Tuner p f) = Tuner p f' where- f' x = convertProposalSimple l $ f x+ f' x = convertProposalSimple jf l $ f x -- | Tune the proposal? data Tune = Tune | NoTune deriving (Show, Eq) +-- | Tuning parameter.+type TuningParameter = Double+ -- | Create a tuneable proposal. createProposal :: -- | Description of the proposal type and parameters. PDescription -> -- | Function creating a simple proposal for a given tuning parameter. The- -- larger the tuning parameter, the larger the proposal (and the lower the- -- expected acceptance rate), and vice versa.- (Double -> ProposalSimple a) ->+ -- larger the tuning parameter, the larger the proposal and the lower the+ -- expected acceptance rate; and vice versa.+ (TuningParameter -> ProposalSimple a) -> -- | Dimension. PDimension -> -- | Name.@@ -223,19 +288,34 @@ createProposal r f d n w Tune = Proposal n r d w (f 1.0) (Just $ Tuner 1.0 f) createProposal r f d n w NoTune = Proposal n r d w (f 1.0) Nothing --- Minimal tuning parameter; subject to change.-tuningParamMin :: Double-tuningParamMin = 1e-12+-- | Minimal tuning parameter; @1e-12@, subject to change.+--+-- >>> tuningParameterMin+-- 1e-5+tuningParameterMin :: TuningParameter+tuningParameterMin = 1e-5 --- | Tune a 'Proposal'. Return 'Nothing' if 'Proposal' is not tuneable. The size--- of the proposal is proportional to the tuning parameter. Negative tuning--- parameters are not allowed.-tune :: (Double -> Double) -> Proposal a -> Maybe (Proposal a)+-- | Maximal tuning parameter; @1e12@, subject to change.+-- >>> tuningParameterMax+-- 1e3+tuningParameterMax :: TuningParameter+tuningParameterMax = 1e3++-- | Tune a 'Proposal'.+--+-- The size of the proposal is proportional to the tuning parameter which has a+-- positive lower bound of 'tuningParameterMin'.+--+-- The tuning function maps the current tuning parameter to a new one.+--+-- Return 'Nothing' if 'Proposal' is not tuneable.+tune :: (TuningParameter -> TuningParameter) -> Proposal a -> Maybe (Proposal a) tune f m = do- (Tuner t g) <- pTuner m- -- Ensure that the tuning parameter is strictly positive.- let t' = max tuningParamMin (f t)- return $ m {pSimple = g t', pTuner = Just $ Tuner t' g}+ (Tuner t g) <- prTuner m+ -- Ensure that the tuning parameter is strictly positive and well bounded.+ let t' = max tuningParameterMin (f t)+ t'' = min tuningParameterMax t'+ return $ m {prSimple = g t'', prTuner = Just $ Tuner t'' g} -- | See 'PDimension'. getOptimalRate :: PDimension -> Double@@ -329,8 +409,8 @@ setOrder o c = c {ccOrder = o} -- | Replicate 'Proposal's according to their weights and possibly shuffle them.-orderProposals :: Cycle a -> GenIO -> IO [Proposal a]-orderProposals (Cycle xs o) g = case o of+prepareProposals :: Cycle a -> GenIO -> IO [Proposal a]+prepareProposals (Cycle xs o) g = case o of RandomO -> shuffle ps g SequentialO -> return ps RandomReversibleO -> do@@ -338,7 +418,7 @@ return $ psR ++ reverse psR SequentialReversibleO -> return $ ps ++ reverse ps where- !ps = concat [replicate (fromPWeight $ pWeight p) p | p <- xs]+ !ps = concat [replicate (fromPWeight $ prWeight p) p | p <- xs] -- The number of proposals depends on the order. getNProposalsPerCycle :: Cycle a -> Int@@ -348,10 +428,10 @@ RandomReversibleO -> 2 * once SequentialReversibleO -> 2 * once where- once = sum $ map (fromPWeight . pWeight) xs+ once = sum $ map (fromPWeight . prWeight) xs -- | Tune 'Proposal's in the 'Cycle'. See 'tune'.-tuneCycle :: M.Map (Proposal a) (Double -> Double) -> Cycle a -> Cycle a+tuneCycle :: M.Map (Proposal a) (TuningParameter -> TuningParameter) -> Cycle a -> Cycle a tuneCycle m c = if sort (M.keys m) == sort ps then c {ccProposals = map tuneF ps}@@ -368,9 +448,11 @@ autoTuneCycle :: Acceptance (Proposal a) -> Cycle a -> Cycle a autoTuneCycle a = tuneCycle (M.mapWithKey tuningF $ acceptanceRates a) where- tuningF proposal currentRate currentTuningParam =- let optimalRate = getOptimalRate (pDimension proposal)- in exp (currentRate - optimalRate) * currentTuningParam+ tuningF proposal mCurrentRate currentTuningParam = case mCurrentRate of+ Nothing -> currentTuningParam+ Just currentRate ->+ let optimalRate = getOptimalRate (prDimension proposal)+ in exp (2 * (currentRate - optimalRate)) * currentTuningParam renderRow :: BL.ByteString ->@@ -395,6 +477,67 @@ tp = alignRight 20 tuneParam mt = alignRight 30 manualAdjustment +-- | For each key @k@, store the number of accepted and rejected proposals.+newtype Acceptance k = Acceptance {fromAcceptance :: M.Map k (Int, Int)}+ deriving (Eq, Read, Show)++instance ToJSONKey k => ToJSON (Acceptance k) where+ toJSON (Acceptance m) = toJSON m+ toEncoding (Acceptance m) = toEncoding m++instance (Ord k, FromJSONKey k) => FromJSON (Acceptance k) where+ parseJSON v = Acceptance <$> parseJSON v++-- | In the beginning there was the Word.+--+-- Initialize an empty storage of accepted/rejected values.+emptyA :: Ord k => [k] -> Acceptance k+emptyA ks = Acceptance $ M.fromList [(k, (0, 0)) | k <- ks]++-- | For key @k@, prepend an accepted (True) or rejected (False) proposal.+pushA :: Ord k => k -> Bool -> Acceptance k -> Acceptance k+pushA k True = Acceptance . M.adjust (force . first succ) k . fromAcceptance+pushA k False = Acceptance . M.adjust (force . second succ) k . fromAcceptance+{-# INLINEABLE pushA #-}++-- | Reset acceptance storage.+resetA :: Ord k => Acceptance k -> Acceptance k+resetA = emptyA . M.keys . fromAcceptance++transformKeys :: (Ord k1, Ord k2) => [k1] -> [k2] -> M.Map k1 v -> M.Map k2 v+transformKeys ks1 ks2 m = foldl' insrt M.empty $ zip ks1 ks2+ where+ insrt m' (k1, k2) = M.insert k2 (m M.! k1) m'++-- | Transform keys using the given lists. Keys not provided will not be present+-- in the new 'Acceptance' variable.+transformKeysA :: (Ord k1, Ord k2) => [k1] -> [k2] -> Acceptance k1 -> Acceptance k2+transformKeysA ks1 ks2 = Acceptance . transformKeys ks1 ks2 . fromAcceptance++-- | Acceptance counts and rate for a specific proposal.+--+-- Return 'Nothing' if no proposals have been accepted or rejected (division by+-- zero).+acceptanceRate :: Ord k => k -> Acceptance k -> Maybe (Int, Int, Double)+acceptanceRate k a = case fromAcceptance a M.!? k of+ Just (0, 0) -> Nothing+ Just (as, rs) -> Just (as, rs, fromIntegral as / fromIntegral (as + rs))+ Nothing -> error "acceptanceRate: Key not found in map."++-- | Acceptance rates for all proposals.+--+-- Set rate to 'Nothing' if no proposals have been accepted or rejected+-- (division by zero).+acceptanceRates :: Acceptance k -> M.Map k (Maybe Double)+acceptanceRates =+ M.map+ ( \(as, rs) ->+ if as + rs == 0+ then Nothing+ else Just $ fromIntegral as / fromIntegral (as + rs)+ )+ . fromAcceptance+ -- | Header of proposal summaries. proposalHeader :: BL.ByteString proposalHeader =@@ -418,12 +561,11 @@ PName -> PDescription -> PWeight ->- -- Tuning parameter.- Maybe Double ->+ Maybe TuningParameter -> PDimension -> Maybe (Int, Int, Double) -> BL.ByteString-summarizeProposal name description weight tuningParam dimension r =+summarizeProposal name description weight tuningParameter dimension ar = renderRow (BL.pack $ fromPName name) (BL.pack $ fromPDescription description)@@ -436,16 +578,27 @@ manualAdjustmentStr where weightStr = BB.toLazyByteString $ BB.intDec $ fromPWeight weight- nAccept = BB.toLazyByteString $ maybe "" (BB.intDec . (^. _1)) r- nReject = BB.toLazyByteString $ maybe "" (BB.intDec . (^. _2)) r- acceptRate = BL.fromStrict $ maybe "" (BC.toFixed 2 . (^. _3)) r+ nAccept = BB.toLazyByteString $ maybe "" (BB.intDec . (^. _1)) ar+ nReject = BB.toLazyByteString $ maybe "" (BB.intDec . (^. _2)) ar+ acceptRate = BL.fromStrict $ maybe "" (BC.toFixed 2 . (^. _3)) ar optimalRate = BL.fromStrict $ BC.toFixed 2 $ getOptimalRate dimension- tuneParamStr = BL.fromStrict $ maybe "" (BC.toFixed 3) tuningParam- check v- | v < rateMin = "rate too low"- | v > rateMax = "rate too high"- | otherwise = ""- manualAdjustmentStr = BL.fromStrict $ maybe "" (check . (^. _3)) r+ tuneParamStr = BL.fromStrict $ maybe "" (BC.toFixed 3) tuningParameter+ checkRate rate+ | rate < rateMin = Just "rate too low"+ | rate > rateMax = Just "rate too high"+ | otherwise = Nothing+ checkTuningParam tp+ | tp <= (1.1 * tuningParameterMin) = Just "tuning parameter too low"+ | tp >= (0.9 * tuningParameterMax) = Just "tuning parameter too high"+ | otherwise = Nothing+ tps = checkTuningParam =<< tuningParameter+ ars = (checkRate . (^. _3)) =<< ar+ manualAdjustmentStr =+ let+ in case (ars, tps) of+ (Nothing, Nothing) -> ""+ (Just s, _) -> s+ (_, Just s) -> s -- | Summarize the 'Proposal's in the 'Cycle'. Also report acceptance rates. summarizeCycle :: Acceptance (Proposal a) -> Cycle a -> BL.ByteString@@ -458,11 +611,11 @@ proposalHLine ] ++ [ summarizeProposal- (pName p)- (pDescription p)- (pWeight p)- (tParam <$> pTuner p)- (pDimension p)+ (prName p)+ (prDescription p)+ (prWeight p)+ (tParam <$> prTuner p)+ (prDimension p) (ar p) | p <- ps ]@@ -475,51 +628,3 @@ 1 -> nProposalsStr <> " proposal is performed per iteration." _ -> nProposalsStr <> " proposals are performed per iterations." ar m = acceptanceRate m a---- | For each key @k@, store the number of accepted and rejected proposals.-newtype Acceptance k = Acceptance {fromAcceptance :: M.Map k (Int, Int)}- deriving (Eq, Read, Show)--instance ToJSONKey k => ToJSON (Acceptance k) where- toJSON (Acceptance m) = toJSON m- toEncoding (Acceptance m) = toEncoding m--instance (Ord k, FromJSONKey k) => FromJSON (Acceptance k) where- parseJSON v = Acceptance <$> parseJSON v---- | In the beginning there was the Word.------ Initialize an empty storage of accepted/rejected values.-emptyA :: Ord k => [k] -> Acceptance k-emptyA ks = Acceptance $ M.fromList [(k, (0, 0)) | k <- ks]---- | For key @k@, prepend an accepted (True) or rejected (False) proposal.-pushA :: Ord k => k -> Bool -> Acceptance k -> Acceptance k-pushA k True = Acceptance . M.adjust (force . first succ) k . fromAcceptance-pushA k False = Acceptance . M.adjust (force . second succ) k . fromAcceptance-{-# INLINEABLE pushA #-}---- | Reset acceptance storage.-resetA :: Ord k => Acceptance k -> Acceptance k-resetA = emptyA . M.keys . fromAcceptance--transformKeys :: (Ord k1, Ord k2) => [k1] -> [k2] -> M.Map k1 v -> M.Map k2 v-transformKeys ks1 ks2 m = foldl' insrt M.empty $ zip ks1 ks2- where- insrt m' (k1, k2) = M.insert k2 (m M.! k1) m'---- | Transform keys using the given lists. Keys not provided will not be present--- in the new 'Acceptance' variable.-transformKeysA :: (Ord k1, Ord k2) => [k1] -> [k2] -> Acceptance k1 -> Acceptance k2-transformKeysA ks1 ks2 = Acceptance . transformKeys ks1 ks2 . fromAcceptance---- | Acceptance counts and rate for a specific proposal.-acceptanceRate :: Ord k => k -> Acceptance k -> Maybe (Int, Int, Double)-acceptanceRate k a = case fromAcceptance a M.!? k of- Just (0, 0) -> Nothing- Just (as, rs) -> Just (as, rs, fromIntegral as / fromIntegral (as + rs))- Nothing -> error "acceptanceRate: Key not found in map."---- | Acceptance rates for all proposals.-acceptanceRates :: Acceptance k -> M.Map k Double-acceptanceRates = M.map (\(as, rs) -> fromIntegral as / fromIntegral (as + rs)) . fromAcceptance
src/Mcmc/Proposal/Bactrian.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Proposal.Bactrian -- Description : Bactrian proposals--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -12,21 +12,26 @@ -- -- See https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3845170/. module Mcmc.Proposal.Bactrian- ( slideBactrian,+ ( SpikeParameter,+ slideBactrian, scaleBactrian, ) where import Mcmc.Proposal+import Mcmc.Statistics.Types import Numeric.Log import Statistics.Distribution import Statistics.Distribution.Normal import System.Random.MWC import System.Random.MWC.Distributions +-- | Type synonym indicating the spike parameter.+type SpikeParameter = Double+ genBactrian ::- Double ->- Double ->+ SpikeParameter ->+ StandardDeviation -> GenIO -> IO Double genBactrian m s g = do@@ -37,7 +42,7 @@ b <- bernoulli 0.5 g return $ if b then x else - x -logDensityBactrian :: Double -> Double -> Double -> Log Double+logDensityBactrian :: SpikeParameter -> StandardDeviation -> Double -> Log Double logDensityBactrian m s x = Exp $ log $ kernel1 + kernel2 where mn = m * s@@ -48,20 +53,18 @@ kernel2 = density dist2 x bactrianAdditive ::- Double ->- Double ->- Double ->- GenIO ->- IO (Double, Log Double, Log Double)+ SpikeParameter ->+ StandardDeviation ->+ ProposalSimple Double bactrianAdditive m s x g = do dx <- genBactrian m s g return (x + dx, 1.0, 1.0) -- bactrianSimple lens spike stdDev tune forwardOp backwardOp bactrianAdditiveSimple ::- Double ->- Double ->- Double ->+ SpikeParameter ->+ StandardDeviation ->+ TuningParameter -> ProposalSimple Double bactrianAdditiveSimple m s t | m < 0 = error "bactrianAdditiveSimple: Spike parameter negative."@@ -81,15 +84,10 @@ -- -- See https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3845170/. slideBactrian ::- -- | Spike parameter \(m\).- Double ->- -- | Standard deviation \(s\).- Double ->- -- | Name.+ SpikeParameter ->+ StandardDeviation -> PName ->- -- | Weight. PWeight ->- -- | Enable tuning. Tune -> Proposal Double slideBactrian m s = createProposal description (bactrianAdditiveSimple m s) (PDimension 1)@@ -106,11 +104,9 @@ fInv dx = recip (1 - dx) - 1 bactrianMult ::- Double ->- Double ->- Double ->- GenIO ->- IO (Double, Log Double, Log Double)+ SpikeParameter ->+ StandardDeviation ->+ ProposalSimple Double bactrianMult m s x g = do du <- genBactrian m s g let qXY = logDensityBactrian m s du@@ -119,7 +115,7 @@ jac = Exp $ log $ recip u return (x * u, qYX / qXY, jac) -bactrianMultSimple :: Double -> Double -> Double -> ProposalSimple Double+bactrianMultSimple :: SpikeParameter -> StandardDeviation -> TuningParameter -> ProposalSimple Double bactrianMultSimple m s t | m < 0 = error "bactrianMultSimple: Spike parameter negative." | m >= 1 = error "bactrianMultSimple: Spike parameter 1.0 or larger."@@ -129,15 +125,10 @@ -- | Multiplicative proposal with kernel similar to the silhouette of a Bactrian -- camel. See 'slideBactrian'. scaleBactrian ::- -- | Spike parameter.- Double ->- -- | Standard deviation.- Double ->- -- | Name.+ SpikeParameter ->+ StandardDeviation -> PName ->- -- | Weight. PWeight ->- -- | Enable tuning. Tune -> Proposal Double scaleBactrian m s = createProposal description (bactrianMultSimple m s) (PDimension 1)
src/Mcmc/Proposal/Generic.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.Proposal.Generic -- Description : Generic interface for creating proposals--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -51,7 +51,7 @@ -- -- Conversion to log domain is necessary, because some determinants of -- Jacobians are very small (or large).- Maybe (a -> Double -> Log Double) ->+ Maybe (a -> Double -> Jacobian) -> ProposalSimple a genericContinuous d f mInv mJac x g = do u <- genContVar d g
src/Mcmc/Proposal/Scale.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Proposal.Scale -- Description : Multiplicative proposals--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -20,12 +20,13 @@ import Mcmc.Proposal import Mcmc.Proposal.Generic+import Mcmc.Statistics.Types import Numeric.Log import Statistics.Distribution.Gamma -- The actual proposal with tuning parameter. The tuning parameter does not -- change the mean.-scaleSimple :: Double -> Double -> Double -> ProposalSimple Double+scaleSimple :: Shape -> Scale -> TuningParameter -> ProposalSimple Double scaleSimple k th t = genericContinuous (gammaDistr (k / t) (th * t))@@ -37,10 +38,8 @@ -- | Multiplicative proposal with gamma distributed kernel. scale ::- -- | Shape.- Double ->- -- | Scale.- Double ->+ Shape ->+ Scale -> PName -> PWeight -> Tune ->@@ -54,8 +53,7 @@ -- The scale of the gamma distribution is set to (shape)^{-1}, so that the mean -- of the gamma distribution is 1.0. scaleUnbiased ::- -- | Shape.- Double ->+ Shape -> PName -> PWeight -> Tune ->@@ -64,7 +62,7 @@ where description = PDescription $ "Scale unbiased; shape: " ++ show k -scaleContrarilySimple :: Double -> Double -> Double -> ProposalSimple (Double, Double)+scaleContrarilySimple :: Shape -> Scale -> TuningParameter -> ProposalSimple (Double, Double) scaleContrarilySimple k th t = genericContinuous (gammaDistr (k / t) (th * t))@@ -80,10 +78,8 @@ -- The two values are scaled contrarily so that their product stays constant. -- Contrary proposals are useful when parameters are confounded. scaleContrarily ::- -- | Shape.- Double ->- -- | Scale.- Double ->+ Shape ->+ Scale -> PName -> PWeight -> Tune ->
src/Mcmc/Proposal/Simplex.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Proposal.Simplex -- Description : Proposals on simplices--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -27,6 +27,7 @@ import Data.Aeson.TH import qualified Data.Vector.Unboxed as V import Mcmc.Proposal+import Mcmc.Statistics.Types import Numeric.Log import Statistics.Distribution import Statistics.Distribution.Beta@@ -77,11 +78,12 @@ -- | Create the uniform element of the K-dimensional simplex. -- -- Set all values to \(1/D\).-simplexUniform :: Int -> Simplex+simplexUniform :: Dimension -> Simplex+-- Should never fail. simplexUniform k = either error id $ simplexFromVector $ V.replicate k (1.0 / fromIntegral k) -- Tuning function is inverted (high alpha means small steps).-getTuningFunction :: Double -> (Double -> Double)+getTuningFunction :: TuningParameter -> (TuningParameter -> TuningParameter) getTuningFunction t = (/ t'') where -- Start with small steps.@@ -98,7 +100,7 @@ -- The values determining the proposal size have been set using an example -- analysis. They are good values for this analysis, but may fail for other -- analyses.-dirichletSimple :: Double -> ProposalSimple Simplex+dirichletSimple :: TuningParameter -> ProposalSimple Simplex dirichletSimple t (SimplexUnsafe xs) g = do -- If @t@ is high and above 1.0, the parameter vector will be low, and the -- variance will be high. If @t@ is low and below 1.0, the parameter vector@@ -135,7 +137,7 @@ -- The proposal dimension, which is the dimension of the simplex, is used to -- determine the optimal acceptance rate. ----- For high dimensional simplices, this proposal may have low acceptance ratios.+-- For high dimensional simplices, this proposal may have low acceptance rates. -- In this case, please see the coordinate wise 'beta' proposal. dirichlet :: PDimension -> PName -> PWeight -> Tune -> Proposal Simplex dirichlet = createProposal (PDescription "Dirichlet") dirichletSimple@@ -147,7 +149,7 @@ -- analyses. -- -- See also the 'dirichlet' proposal.-betaSimple :: Int -> Double -> ProposalSimple Simplex+betaSimple :: Dimension -> TuningParameter -> ProposalSimple Simplex betaSimple i t (SimplexUnsafe xs) g = do -- Shape parameters of beta distribution. Do not assume that the sum of the -- elements of 'xs' is 1.0, because then repeated proposals let the sum of the@@ -203,7 +205,7 @@ -- -- This proposal has been assigned a dimension of 2. See the discussion at -- 'PDimension'.-beta :: Int -> PName -> PWeight -> Tune -> Proposal Simplex+beta :: Dimension -> PName -> PWeight -> Tune -> Proposal Simplex beta i = createProposal description (betaSimple i) (PDimension 2) where description = PDescription $ "Beta; coordinate: " ++ show i
src/Mcmc/Proposal/Slide.hs view
@@ -3,7 +3,7 @@ -- | -- Module : Mcmc.Proposal.Slide -- Description : Additive proposals--- Copyright : (c) Dominik Schrempf 2020+-- Copyright : (c) Dominik Schrempf 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -21,25 +21,21 @@ import Mcmc.Proposal import Mcmc.Proposal.Generic+import Mcmc.Statistics.Types import Statistics.Distribution.Normal import Statistics.Distribution.Uniform -- The actual proposal with tuning parameter.-slideSimple :: Double -> Double -> Double -> ProposalSimple Double+slideSimple :: Mean -> StandardDeviation -> TuningParameter -> ProposalSimple Double slideSimple m s t = genericContinuous (normalDistr m (s * t)) (+) (Just negate) Nothing -- | Additive proposal with normally distributed kernel. slide ::- -- | Mean.- Double ->- -- | Standard deviation.- Double ->- -- | Name.+ Mean ->+ StandardDeviation -> PName ->- -- | Weight. PWeight ->- -- | Enable tuning. Tune -> Proposal Double slide m s = createProposal description (slideSimple m s) (PDimension 1)@@ -47,7 +43,7 @@ description = PDescription $ "Slide; mean: " ++ show m ++ ", sd: " ++ show s -- The actual proposal with tuning parameter.-slideSymmetricSimple :: Double -> Double -> ProposalSimple Double+slideSymmetricSimple :: StandardDeviation -> TuningParameter -> ProposalSimple Double slideSymmetricSimple s t = genericContinuous (normalDistr 0.0 (s * t)) (+) Nothing Nothing @@ -55,13 +51,9 @@ -- proposal is very fast, because the Metropolis-Hastings-Green ratio does not -- include calculation of the forwards and backwards kernels. slideSymmetric ::- -- | Standard deviation.- Double ->- -- | Name.+ StandardDeviation -> PName ->- -- | Weight. PWeight ->- -- | Enable tuning. Tune -> Proposal Double slideSymmetric s = createProposal description (slideSymmetricSimple s) (PDimension 1)@@ -69,7 +61,7 @@ description = PDescription $ "Slide symmetric; sd: " ++ show s -- The actual proposal with tuning parameter.-slideUniformSimple :: Double -> Double -> ProposalSimple Double+slideUniformSimple :: Size -> TuningParameter -> ProposalSimple Double slideUniformSimple d t = genericContinuous (uniformDistr (- t * d) (t * d)) (+) Nothing Nothing @@ -77,13 +69,9 @@ -- proposal is very fast, because the Metropolis-Hastings-Green ratio does not -- include calculation of the forwards and backwards kernels. slideUniformSymmetric ::- -- | Delta.- Double ->- -- | Name.+ Size -> PName ->- -- | Weight. PWeight ->- -- | Enable tuning. Tune -> Proposal Double slideUniformSymmetric d = createProposal description (slideUniformSimple d) (PDimension 1)@@ -93,7 +81,11 @@ contra :: (Double, Double) -> Double -> (Double, Double) contra (x, y) u = (x + u, y - u) -slideContrarilySimple :: Double -> Double -> Double -> ProposalSimple (Double, Double)+slideContrarilySimple ::+ Mean ->+ StandardDeviation ->+ TuningParameter ->+ ProposalSimple (Double, Double) slideContrarilySimple m s t = genericContinuous (normalDistr m (s * t)) contra (Just negate) Nothing @@ -102,15 +94,10 @@ -- The two values are slid contrarily so that their sum stays constant. Contrary -- proposals are useful when parameters are confounded. slideContrarily ::- -- | Mean.- Double ->- -- | Standard deviation.- Double ->- -- | Name.+ Mean ->+ StandardDeviation -> PName ->- -- | Weight. PWeight ->- -- | Enable tuning. Tune -> Proposal (Double, Double) slideContrarily m s = createProposal description (slideContrarilySimple m s) (PDimension 2)
src/Mcmc/Settings.hs view
@@ -1,10 +1,11 @@+{-# LANGUAGE DerivingVia #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} -- | -- Module : Mcmc.Settings -- Description : Settings of Markov chain Monte Carlo samplers--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -15,13 +16,17 @@ module Mcmc.Settings ( -- * Data types AnalysisName (..),+ HasAnalysisName (..), BurnInSpecification (..), burnInIterations, Iterations (..),+ TraceLength (..), ExecutionMode (..),+ HasExecutionMode (..), openWithExecutionMode, ParallelizationMode (..), SaveMode (..),+ LogMode (..), Verbosity (..), -- * Settings@@ -35,15 +40,21 @@ import Data.Aeson import Data.Aeson.TH import qualified Data.ByteString.Lazy.Char8 as BL+import Mcmc.Logger import System.Directory import System.IO -- | Analysis name of the MCMC sampler. newtype AnalysisName = AnalysisName {fromAnalysisName :: String} deriving (Eq, Read, Show)+ deriving (Monoid, Semigroup) via String $(deriveJSON defaultOptions ''AnalysisName) +-- | Types with analysis names.+class HasAnalysisName s where+ getAnalysisName :: s -> AnalysisName+ -- | Burn in specification. data BurnInSpecification = -- | No burn in.@@ -53,15 +64,32 @@ | -- | Burn in for a given number of iterations. Enable auto tuning with a -- given period. BurnInWithAutoTuning Int Int+ | -- | Burn in with the given list of auto tuning periods.+ --+ -- For example, @BurnInWithCustomAutoTuning [100,200]@ performs 300+ -- iterations with two auto tuning steps. One after 100 iterations, the+ -- second one after 200 more iterations.+ --+ -- Usually it is useful to auto tune more frequently in the beginning of the+ -- MCMC run.+ BurnInWithCustomAutoTuning [Int] deriving (Eq, Read, Show) $(deriveJSON defaultOptions ''BurnInSpecification) +-- Check if the burn in specification is valid.+burnInValid :: BurnInSpecification -> Bool+burnInValid NoBurnIn = True+burnInValid (BurnInWithoutAutoTuning n) = n > 0+burnInValid (BurnInWithAutoTuning n t) = n > 0 && t > 0+burnInValid (BurnInWithCustomAutoTuning xs) = not (null xs) && all (> 0) xs+ -- | Get the number of burn in iterations. burnInIterations :: BurnInSpecification -> Int burnInIterations NoBurnIn = 0 burnInIterations (BurnInWithoutAutoTuning n) = n burnInIterations (BurnInWithAutoTuning n _) = n+burnInIterations (BurnInWithCustomAutoTuning xs) = sum xs -- | Number of normal iterations after burn in. --@@ -71,6 +99,17 @@ $(deriveJSON defaultOptions ''Iterations) +-- | The length of the stored "Mcmc.Chain.Trace".+--+-- Be careful, this setting determines the memory requirement of the MCMC chain.+data TraceLength+ = -- | Automatically determine the length of the trace. The value is+ -- determined by the 'Mcmc.Monitor.MonitorBatch' with largest batch size.+ TraceAuto+ | -- | Store a given minimum number of iterations of the chain. Store more+ -- iterations if required (see 'TraceAuto').+ TraceMinimum Int+ -- | Execution mode. data ExecutionMode = -- | Perform new run.@@ -89,6 +128,10 @@ $(deriveJSON defaultOptions ''ExecutionMode) +-- | Types with execution modes.+class HasExecutionMode s where+ getExecutionMode :: s -> ExecutionMode+ -- | Open a file honoring the execution mode. -- -- Call 'error' if execution mode is@@ -121,17 +164,16 @@ -- | Parallelization mode. -- -- Parallel execution of the chains is only beneficial when the algorithm allows--- for parallelization, and if computation of the next iteration takes a long+-- for parallelization, and if computation of the next iteration takes some -- time. If the calculation of the next state is fast, sequential execution is--- usually beneficial, even for algorithms involving parallel chains. If the--- calculation of the next state is slow, parallel execution may be beneficial.+-- usually beneficial, even for algorithms involving parallel chains. ----- - The "Mcmc.Algorithm.Metropolis" algorithm is inherently sequential.+-- - The "Mcmc.Algorithm.MHG" algorithm is inherently sequential. -- -- - The "Mcmc.Algorithm.MC3" algorithm works well with parallelization. -- -- Of course, also the prior or likelihood functions can be computed in--- parallel. However, this library is not aware of how these functions are+-- parallel. However, this library is unaware about how these functions are -- computed. data ParallelizationMode = Sequential@@ -140,20 +182,18 @@ $(deriveJSON defaultOptions ''ParallelizationMode) --- | Should the MCMC run be saved at the end of the run?-data SaveMode = NoSave | Save+-- | Define information stored on disk.+data SaveMode+ = -- | Do not save the MCMC analysis. The analysis can not be continued.+ NoSave+ | -- | Save the MCMC analysis so that it can be continued. This can be slow,+ -- if the trace is long, or if the states are large objects. See+ -- 'TraceLength'.+ Save deriving (Eq, Read, Show) $(deriveJSON defaultOptions ''SaveMode) --- $(deriveJSON defaultOptions ''CleaningMode)---- | Not much to say here.-data Verbosity = Quiet | Warn | Info | Debug- deriving (Eq, Ord, Read, Show)--$(deriveJSON defaultOptions ''Verbosity)- -- | Settings of an MCMC sampler. data Settings = Settings { sAnalysisName :: AnalysisName,@@ -162,14 +202,27 @@ sExecutionMode :: ExecutionMode, sParallelizationMode :: ParallelizationMode, sSaveMode :: SaveMode,+ sLogMode :: LogMode, sVerbosity :: Verbosity } deriving (Eq, Show) +instance HasAnalysisName Settings where+ getAnalysisName = sAnalysisName++instance HasExecutionMode Settings where+ getExecutionMode = sExecutionMode++instance HasLogMode Settings where+ getLogMode = sLogMode++instance HasVerbosity Settings where+ getVerbosity = sVerbosity+ $(deriveJSON defaultOptions ''Settings) settingsFn :: String -> FilePath-settingsFn n = n ++ ".settings"+settingsFn n = n ++ ".mcmc.settings" -- | Save settings to a file determined by the analysis name. settingsSave :: Settings -> IO ()@@ -217,10 +270,10 @@ -- | Current iteration. Int -> IO ()-settingsCheck s@(Settings nm bi i em _ _ _) iCurrent+settingsCheck s@(Settings nm bi i em _ _ _ _) iCurrent | null (fromAnalysisName nm) = serr "Analysis name is the empty string." | burnInIterations bi < 0 = serr "Number of burn in iterations is negative."- | not $ burnInAutoTuningPeriodValid bi = serr "Auto tuning period is zero or negative."+ | not $ burnInValid bi = serr $ "Burn in setting invalid: " <> show bi | fromIterations i < 0 = serr "Number of iterations is negative." | burnInIterations bi + fromIterations i - iCurrent < 0 = serr "Current iteration is larger than the total number of iterations."@@ -231,6 +284,3 @@ | otherwise = return () where serr = settingsError s iCurrent- burnInAutoTuningPeriodValid :: BurnInSpecification -> Bool- burnInAutoTuningPeriodValid (BurnInWithAutoTuning _ t) = t > 0- burnInAutoTuningPeriodValid _ = True
+ src/Mcmc/Statistics/Types.hs view
@@ -0,0 +1,56 @@+-- |+-- Module : Mcmc.Statistics.Types+-- Description : Types indicating properties of distributions+-- Copyright : (c) 2021 Dominik Schrempf+-- License : GPL-3.0-or-later+--+-- Maintainer : dominik.schrempf@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- Creation date: Tue Feb 23 14:51:06 2021.+module Mcmc.Statistics.Types+ ( Mean,+ StandardDeviation,+ Variance,+ Shape,+ Scale,+ Rate,+ Dimension,+ Size,+ LowerBoundary,+ UpperBoundary,+ )+where++-- | Mean of a distribution.+type Mean = Double++-- | Standard deviation of a distribution.+type StandardDeviation = Double++-- | Variance of a distribution.+type Variance = Double++-- | Shape of a distribution.+type Shape = Double++-- | Scale of a distribution.+type Scale = Double++-- | Rate of a distribution.+type Rate = Double++-- | Dimension of a distribution.+type Dimension = Int++-- | Size of a distribution.+--+-- For example, the size of the interval of the uniform distribution.+type Size = Double++-- | Lower boundary of a distribution.+type LowerBoundary = Double++-- | Upper boundary of a distribution.+type UpperBoundary = Double
test/Mcmc/ProposalSpec.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.ProposalSpec -- Description : Unit tests for Mcmc.Proposal--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -20,24 +20,24 @@ import Test.Hspec p1 :: Proposal Double-p1 = slideSymmetric 1.0 (PName "Test 1") (PWeight 1) Tune+p1 = slideSymmetric 1.0 (PName "Test 1") (pWeight 1) Tune p2 :: Proposal Double-p2 = slideSymmetric 1.0 (PName "Test 2") (PWeight 3) Tune+p2 = slideSymmetric 1.0 (PName "Test 2") (pWeight 3) Tune c :: Cycle Double c = cycleFromList [p1, p2] spec :: Spec spec =- describe "orderProposals" $+ describe "prepareProposals" $ it "returns the correct number of proposals in a cycle" $ do g <- create- l1 <- length <$> orderProposals c g+ l1 <- length <$> prepareProposals c g l1 `shouldBe` 4- l2 <- length <$> orderProposals (setOrder RandomReversibleO c) g+ l2 <- length <$> prepareProposals (setOrder RandomReversibleO c) g l2 `shouldBe` 8- o3 <- orderProposals (setOrder SequentialReversibleO c) g+ o3 <- prepareProposals (setOrder SequentialReversibleO c) g length o3 `shouldBe` 8 o3 == [p1, p2, p2, p2, p2, p2, p2, p1] `shouldBe` True
test/Mcmc/SaveSpec.hs view
@@ -1,7 +1,7 @@ -- | -- Module : Mcmc.SaveSpec -- Description : Unit tests for Mcmc.Save--- Copyright : (c) Dominik Schrempf, 2020+-- Copyright : (c) Dominik Schrempf, 2021 -- License : GPL-3.0-or-later -- -- Maintainer : dominik.schrempf@gmail.com@@ -18,7 +18,6 @@ import Mcmc.Chain.Chain import Mcmc.Chain.Save import Mcmc.Chain.Trace-import Numeric.Log import Statistics.Distribution import Statistics.Distribution.Normal import qualified System.Random.MWC as R@@ -36,10 +35,10 @@ proposals :: Cycle Double proposals = cycleFromList- [ slideSymmetric 0.1 (PName "Small") (PWeight 5) Tune,- slideSymmetric 1.0 (PName "Medium") (PWeight 2) Tune,- slideSymmetric 5.0 (PName "Large") (PWeight 2) Tune,- slide 1.0 4.0 (PName "Skewed") (PWeight 1) Tune+ [ slideSymmetric 0.1 (PName "Small") (pWeight 5) Tune,+ slideSymmetric 1.0 (PName "Medium") (pWeight 2) Tune,+ slideSymmetric 5.0 (PName "Large") (pWeight 2) Tune,+ slide 1.0 4.0 (PName "Skewed") (pWeight 1) Tune ] monStd :: MonitorStdOut Double@@ -62,8 +61,9 @@ Overwrite Sequential NoSave+ LogStdOutOnly Quiet- c <- fromMHG <$> mhg noPrior lh proposals mon 0 gen+ c <- fromMHG <$> mhg noPrior lh proposals mon TraceAuto 0 gen savedChain <- toSavedChain c c' <- fromSavedChain noPrior lh proposals mon savedChain putStrLn "@load . save@ should be @id@."@@ -87,7 +87,7 @@ g2' <- R.save $ generator r' g2 `shouldBe` g2' --- -- TODO.+-- -- TODO: 'mhContinue'. -- describe "mhContinue" -- $ it "mh 200 + mhContinue 200 == mh 400" -- $ do