packages feed

mcmc 0.5.0.0 → 0.6.0.0

raw patch · 32 files changed

+1765/−672 lines, 32 filesdep +math-functionsdep +matricesdep ~aesondep ~bytestringdep ~circularPVP ok

version bump matches the API change (PVP)

Dependencies added: math-functions, matrices

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.Chain.Chain: noLikelihood :: LikelihoodFunction a
- Mcmc.Chain.Chain: noPrior :: PriorFunction a
- Mcmc.Chain.Chain: type LikelihoodFunction a = a -> Log Double
- Mcmc.Chain.Chain: type PriorFunction a = a -> Prior
- Mcmc.Prior: largerThan :: LowerBoundary -> PriorFunction Double
- Mcmc.Prior: lowerThan :: UpperBoundary -> PriorFunction Double
- Mcmc.Proposal: RandomO :: Order
- Mcmc.Proposal: RandomReversibleO :: Order
- Mcmc.Proposal: SequentialO :: Order
- Mcmc.Proposal: SequentialReversibleO :: Order
- Mcmc.Proposal: [tFunc] :: Tuner a -> TuningParameter -> ProposalSimple a
- Mcmc.Proposal: [tParam] :: Tuner a -> TuningParameter
- Mcmc.Proposal: acceptanceRate :: Ord k => k -> Acceptance k -> Maybe (Int, Int, Double)
- Mcmc.Proposal: acceptanceRates :: Acceptance k -> Map k (Maybe Double)
- Mcmc.Proposal: autoTuneCycle :: Acceptance (Proposal a) -> Cycle a -> Cycle a
- Mcmc.Proposal: cycleFromList :: [Proposal a] -> Cycle a
- Mcmc.Proposal: data Acceptance k
- Mcmc.Proposal: data Cycle a
- Mcmc.Proposal: data Order
- Mcmc.Proposal: emptyA :: Ord k => [k] -> Acceptance k
- Mcmc.Proposal: instance (GHC.Classes.Ord k, Data.Aeson.Types.FromJSON.FromJSONKey k) => Data.Aeson.Types.FromJSON.FromJSON (Mcmc.Proposal.Acceptance k)
- Mcmc.Proposal: instance (GHC.Classes.Ord k, GHC.Read.Read k) => GHC.Read.Read (Mcmc.Proposal.Acceptance k)
- Mcmc.Proposal: instance Data.Aeson.Types.ToJSON.ToJSONKey k => Data.Aeson.Types.ToJSON.ToJSON (Mcmc.Proposal.Acceptance k)
- Mcmc.Proposal: instance Data.Default.Class.Default Mcmc.Proposal.Order
- Mcmc.Proposal: instance GHC.Classes.Eq Mcmc.Proposal.Order
- Mcmc.Proposal: instance GHC.Classes.Eq k => GHC.Classes.Eq (Mcmc.Proposal.Acceptance k)
- Mcmc.Proposal: instance GHC.Show.Show Mcmc.Proposal.Order
- Mcmc.Proposal: instance GHC.Show.Show k => GHC.Show.Show (Mcmc.Proposal.Acceptance k)
- Mcmc.Proposal: prepareProposals :: Cycle a -> GenIO -> IO [Proposal a]
- Mcmc.Proposal: proposalHLine :: ByteString
- Mcmc.Proposal: pushA :: Ord k => k -> Bool -> Acceptance k -> Acceptance k
- Mcmc.Proposal: resetA :: Ord k => Acceptance k -> Acceptance k
- Mcmc.Proposal: setOrder :: Order -> Cycle a -> Cycle a
- Mcmc.Proposal: summarizeCycle :: Acceptance (Proposal a) -> Cycle a -> ByteString
- Mcmc.Proposal: transformKeysA :: (Ord k1, Ord k2) => [k1] -> [k2] -> Acceptance k1 -> Acceptance k2
- Mcmc.Proposal: tune :: (TuningParameter -> TuningParameter) -> Proposal a -> Maybe (Proposal a)
- Mcmc.Proposal: tuneCycle :: Map (Proposal a) (TuningParameter -> TuningParameter) -> Cycle a -> Cycle a
- Mcmc.Settings: data BurnInSpecification
- Mcmc.Settings: instance Data.Aeson.Types.FromJSON.FromJSON Mcmc.Settings.BurnInSpecification
- Mcmc.Settings: instance Data.Aeson.Types.ToJSON.ToJSON Mcmc.Settings.BurnInSpecification
- Mcmc.Settings: instance GHC.Classes.Eq Mcmc.Settings.BurnInSpecification
- Mcmc.Settings: instance GHC.Read.Read Mcmc.Settings.BurnInSpecification
- Mcmc.Settings: instance GHC.Show.Show Mcmc.Settings.BurnInSpecification
+ Mcmc.Acceptance: acceptanceRate :: Ord k => k -> Acceptance k -> Maybe (Int, Int, AcceptanceRate)
+ Mcmc.Acceptance: acceptanceRates :: Acceptance k -> Map k (Maybe AcceptanceRate)
+ Mcmc.Acceptance: data Acceptance k
+ Mcmc.Acceptance: emptyA :: Ord k => [k] -> Acceptance k
+ Mcmc.Acceptance: instance (GHC.Classes.Ord k, Data.Aeson.Types.FromJSON.FromJSONKey k) => Data.Aeson.Types.FromJSON.FromJSON (Mcmc.Acceptance.Acceptance k)
+ Mcmc.Acceptance: instance (GHC.Classes.Ord k, GHC.Read.Read k) => GHC.Read.Read (Mcmc.Acceptance.Acceptance k)
+ Mcmc.Acceptance: instance Data.Aeson.Types.ToJSON.ToJSONKey k => Data.Aeson.Types.ToJSON.ToJSON (Mcmc.Acceptance.Acceptance k)
+ Mcmc.Acceptance: instance GHC.Classes.Eq k => GHC.Classes.Eq (Mcmc.Acceptance.Acceptance k)
+ Mcmc.Acceptance: instance GHC.Show.Show k => GHC.Show.Show (Mcmc.Acceptance.Acceptance k)
+ Mcmc.Acceptance: pushA :: Ord k => k -> Bool -> Acceptance k -> Acceptance k
+ Mcmc.Acceptance: resetA :: Ord k => Acceptance k -> Acceptance k
+ Mcmc.Acceptance: transformKeysA :: (Ord k1, Ord k2) => [k1] -> [k2] -> Acceptance k1 -> Acceptance k2
+ Mcmc.Acceptance: type AcceptanceRate = Double
+ Mcmc.Cycle: RandomO :: Order
+ Mcmc.Cycle: RandomReversibleO :: Order
+ Mcmc.Cycle: SequentialO :: Order
+ Mcmc.Cycle: SequentialReversibleO :: Order
+ Mcmc.Cycle: autoTuneCycle :: Acceptance (Proposal a) -> Vector a -> Cycle a -> Cycle a
+ Mcmc.Cycle: cycleFromList :: [Proposal a] -> Cycle a
+ Mcmc.Cycle: data Cycle a
+ Mcmc.Cycle: data Order
+ Mcmc.Cycle: instance Data.Default.Class.Default Mcmc.Cycle.Order
+ Mcmc.Cycle: instance GHC.Classes.Eq Mcmc.Cycle.Order
+ Mcmc.Cycle: instance GHC.Show.Show Mcmc.Cycle.Order
+ Mcmc.Cycle: prepareProposals :: Cycle a -> GenIO -> IO [Proposal a]
+ Mcmc.Cycle: proposalHLine :: ByteString
+ Mcmc.Cycle: setOrder :: Order -> Cycle a -> Cycle a
+ Mcmc.Cycle: summarizeCycle :: Acceptance (Proposal a) -> Cycle a -> ByteString
+ Mcmc.Internal.Gamma: logGammaG :: RealFloat a => a -> a
+ Mcmc.Likelihood: type LikelihoodFunctionG a b = a -> LikelihoodG b
+ Mcmc.Likelihood: type LikelihoodG a = Log a
+ Mcmc.Posterior: type PosteriorFunction a = PosteriorFunctionG a Double
+ Mcmc.Posterior: type PosteriorFunctionG a b = a -> PosteriorG b
+ Mcmc.Posterior: type PosteriorG a = Log a
+ Mcmc.Prior: greaterThan :: RealFloat a => LowerBoundary a -> PriorFunctionG a a
+ Mcmc.Prior: lessThan :: RealFloat a => UpperBoundary a -> PriorFunctionG a a
+ Mcmc.Prior: type PriorFunctionG a b = a -> PriorG b
+ Mcmc.Prior: type PriorG a = Log a
+ Mcmc.Proposal: PSpecial :: Int -> Double -> PDimension
+ Mcmc.Proposal: [tComputeAuxiliaryTuningParameters] :: Tuner a -> Vector a -> AuxiliaryTuningParameters -> AuxiliaryTuningParameters
+ Mcmc.Proposal: [tComputeTuningParameter] :: Tuner a -> AcceptanceRate -> TuningParameter -> TuningParameter
+ Mcmc.Proposal: [tGetAuxiliaryTuningParameters] :: Tuner a -> AuxiliaryTuningParameters
+ Mcmc.Proposal: [tGetSimpleProposal] :: Tuner a -> TuningParameter -> AuxiliaryTuningParameters -> Either String (ProposalSimple a)
+ Mcmc.Proposal: [tGetTuningParameter] :: Tuner a -> TuningParameter
+ Mcmc.Proposal: defaultTuningFunction :: PDimension -> AcceptanceRate -> TuningParameter -> TuningParameter
+ Mcmc.Proposal: tuneWithChainParameters :: AcceptanceRate -> Vector a -> Proposal a -> Either String (Proposal a)
+ Mcmc.Proposal: tuneWithTuningParameters :: TuningParameter -> AuxiliaryTuningParameters -> Proposal a -> Either String (Proposal a)
+ Mcmc.Proposal: type AuxiliaryTuningParameters = Vector TuningParameter
+ Mcmc.Proposal.Hamiltonian: HNoTune :: HTune
+ Mcmc.Proposal.Hamiltonian: HSettings :: Gradient f -> Maybe (Validate f) -> Masses f -> LeapfrogTrajectoryLength -> LeapfrogScalingFactor -> HTune -> HSettings f
+ Mcmc.Proposal.Hamiltonian: HTuneLeapfrogOnly :: HTune
+ Mcmc.Proposal.Hamiltonian: HTuneMassesAndLeapfrog :: HTune
+ Mcmc.Proposal.Hamiltonian: [hGradient] :: HSettings f -> Gradient f
+ Mcmc.Proposal.Hamiltonian: [hLeapfrogScalingFactor] :: HSettings f -> LeapfrogScalingFactor
+ Mcmc.Proposal.Hamiltonian: [hLeapfrogTrajectoryLength] :: HSettings f -> LeapfrogTrajectoryLength
+ Mcmc.Proposal.Hamiltonian: [hMasses] :: HSettings f -> Masses f
+ Mcmc.Proposal.Hamiltonian: [hMaybeValidate] :: HSettings f -> Maybe (Validate f)
+ Mcmc.Proposal.Hamiltonian: [hTune] :: HSettings f -> HTune
+ Mcmc.Proposal.Hamiltonian: data HSettings f
+ Mcmc.Proposal.Hamiltonian: data HTune
+ Mcmc.Proposal.Hamiltonian: hamiltonian :: (Applicative f, Traversable f) => f Double -> HSettings f -> PName -> PWeight -> Proposal (f Double)
+ Mcmc.Proposal.Hamiltonian: instance GHC.Classes.Eq Mcmc.Proposal.Hamiltonian.HTune
+ Mcmc.Proposal.Hamiltonian: instance GHC.Show.Show Mcmc.Proposal.Hamiltonian.HTune
+ Mcmc.Proposal.Hamiltonian: type Gradient f = f Double -> f Double
+ Mcmc.Proposal.Hamiltonian: type LeapfrogScalingFactor = Double
+ Mcmc.Proposal.Hamiltonian: type LeapfrogTrajectoryLength = Int
+ Mcmc.Proposal.Hamiltonian: type Masses f = f (Maybe Double)
+ Mcmc.Settings: [sTraceLength] :: Settings -> TraceLength
+ Mcmc.Settings: data BurnInSettings
+ Mcmc.Settings: instance Data.Aeson.Types.FromJSON.FromJSON Mcmc.Settings.BurnInSettings
+ Mcmc.Settings: instance Data.Aeson.Types.FromJSON.FromJSON Mcmc.Settings.TraceLength
+ Mcmc.Settings: instance Data.Aeson.Types.ToJSON.ToJSON Mcmc.Settings.BurnInSettings
+ Mcmc.Settings: instance Data.Aeson.Types.ToJSON.ToJSON Mcmc.Settings.TraceLength
+ Mcmc.Settings: instance GHC.Classes.Eq Mcmc.Settings.BurnInSettings
+ Mcmc.Settings: instance GHC.Classes.Eq Mcmc.Settings.TraceLength
+ Mcmc.Settings: instance GHC.Read.Read Mcmc.Settings.BurnInSettings
+ Mcmc.Settings: instance GHC.Show.Show Mcmc.Settings.BurnInSettings
+ Mcmc.Settings: instance GHC.Show.Show Mcmc.Settings.TraceLength
+ Mcmc.Settings: settingsPrettyPrint :: Settings -> ByteString
- Mcmc: scale :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: scale :: Shape Double -> Scale Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: scaleBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: scaleBactrian :: SpikeParameter -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: scaleContrarily :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc: scaleContrarily :: Shape Double -> Scale Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc: scaleUnbiased :: Shape -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: scaleUnbiased :: Shape Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: slide :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: slide :: Mean Double -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: slideBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: slideBactrian :: SpikeParameter -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc: slideContrarily :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc: slideContrarily :: Mean Double -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc: slideSymmetric :: StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc: slideSymmetric :: StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Algorithm: aAutoTune :: Algorithm a => a -> a
+ Mcmc.Algorithm: aAutoTune :: Algorithm a => Int -> a -> IO a
- Mcmc.Algorithm.MC3: mc3 :: MC3Settings -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> TraceLength -> InitialState a -> GenIO -> IO (MC3 a)
+ Mcmc.Algorithm.MC3: mc3 :: MC3Settings -> Settings -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> InitialState a -> GenIO -> IO (MC3 a)
- Mcmc.Algorithm.MHG: mhg :: PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> TraceLength -> InitialState a -> GenIO -> IO (MHG a)
+ Mcmc.Algorithm.MHG: mhg :: Settings -> PriorFunction a -> LikelihoodFunction a -> Cycle a -> Monitor a -> InitialState a -> GenIO -> IO (MHG 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: SavedChain :: Maybe Int -> Link a -> Int -> Stack Vector (Link a) -> Acceptance Int -> Vector Word32 -> [Maybe (TuningParameter, AuxiliaryTuningParameters)] -> SavedChain a
- Mcmc.Chain.Save: [savedTuningParameters] :: SavedChain a -> [Maybe TuningParameter]
+ Mcmc.Chain.Save: [savedTuningParameters] :: SavedChain a -> [Maybe (TuningParameter, AuxiliaryTuningParameters)]
- Mcmc.Likelihood: noLikelihood :: LikelihoodFunction a
+ Mcmc.Likelihood: noLikelihood :: RealFloat b => LikelihoodFunctionG a b
- Mcmc.Likelihood: type LikelihoodFunction a = a -> Log Double
+ Mcmc.Likelihood: type LikelihoodFunction a = LikelihoodFunctionG a Double
- Mcmc.MarginalLikelihood: MLSettings :: AnalysisName -> MLAlgorithm -> NPoints -> BurnInSpecification -> BurnInSpecification -> Iterations -> ExecutionMode -> LogMode -> Verbosity -> MLSettings
+ Mcmc.MarginalLikelihood: MLSettings :: AnalysisName -> MLAlgorithm -> NPoints -> BurnInSettings -> BurnInSettings -> Iterations -> ExecutionMode -> LogMode -> Verbosity -> MLSettings
- Mcmc.MarginalLikelihood: [mlInitialBurnIn] :: MLSettings -> BurnInSpecification
+ Mcmc.MarginalLikelihood: [mlInitialBurnIn] :: MLSettings -> BurnInSettings
- Mcmc.MarginalLikelihood: [mlPointBurnIn] :: MLSettings -> BurnInSpecification
+ Mcmc.MarginalLikelihood: [mlPointBurnIn] :: MLSettings -> BurnInSettings
- Mcmc.Posterior: type Posterior = Log Double
+ Mcmc.Posterior: type Posterior = PosteriorG Double
- Mcmc.Prior: exponential :: Rate -> PriorFunction Double
+ Mcmc.Prior: exponential :: RealFloat a => Rate a -> PriorFunctionG a a
- Mcmc.Prior: gamma :: Shape -> Scale -> PriorFunction Double
+ Mcmc.Prior: gamma :: RealFloat a => Shape a -> Scale a -> PriorFunctionG a a
- Mcmc.Prior: gammaMeanOne :: Shape -> PriorFunction Double
+ Mcmc.Prior: gammaMeanOne :: RealFloat a => Shape a -> PriorFunctionG a a
- Mcmc.Prior: gammaMeanVariance :: Mean -> Variance -> PriorFunction Double
+ Mcmc.Prior: gammaMeanVariance :: RealFloat a => Mean a -> Variance a -> PriorFunctionG a a
- Mcmc.Prior: gammaMeanVarianceToShapeScale :: Mean -> Variance -> (Shape, Scale)
+ Mcmc.Prior: gammaMeanVarianceToShapeScale :: Fractional a => Mean a -> Variance a -> (Shape a, Scale a)
- Mcmc.Prior: gammaShapeScaleToMeanVariance :: Shape -> Scale -> (Mean, Variance)
+ Mcmc.Prior: gammaShapeScaleToMeanVariance :: Num a => Shape a -> Scale a -> (Mean a, Variance a)
- Mcmc.Prior: negative :: PriorFunction Double
+ Mcmc.Prior: negative :: RealFloat a => PriorFunctionG a a
- Mcmc.Prior: noPrior :: PriorFunction a
+ Mcmc.Prior: noPrior :: RealFloat b => PriorFunctionG a b
- Mcmc.Prior: normal :: Mean -> StandardDeviation -> PriorFunction Double
+ Mcmc.Prior: normal :: RealFloat a => Mean a -> StandardDeviation a -> PriorFunctionG a a
- Mcmc.Prior: poisson :: Rate -> PriorFunction Int
+ Mcmc.Prior: poisson :: Rate Double -> PriorFunction Int
- Mcmc.Prior: positive :: PriorFunction Double
+ Mcmc.Prior: positive :: RealFloat a => PriorFunctionG a a
- Mcmc.Prior: product' :: [Log Double] -> Log Double
+ Mcmc.Prior: product' :: RealFloat a => [Log a] -> Log a
- Mcmc.Prior: type Prior = Log Double
+ Mcmc.Prior: type Prior = PriorG Double
- Mcmc.Prior: type PriorFunction a = a -> Prior
+ Mcmc.Prior: type PriorFunction a = PriorFunctionG a Double
- Mcmc.Prior: uniform :: LowerBoundary -> UpperBoundary -> PriorFunction Double
+ Mcmc.Prior: uniform :: RealFloat a => LowerBoundary a -> UpperBoundary a -> PriorFunctionG a a
- Mcmc.Proposal: Tuner :: TuningParameter -> (TuningParameter -> ProposalSimple a) -> Tuner a
+ Mcmc.Proposal: Tuner :: TuningParameter -> (AcceptanceRate -> TuningParameter -> TuningParameter) -> AuxiliaryTuningParameters -> (Vector a -> AuxiliaryTuningParameters -> AuxiliaryTuningParameters) -> (TuningParameter -> AuxiliaryTuningParameters -> Either String (ProposalSimple a)) -> Tuner a
- Mcmc.Proposal.Bactrian: scaleBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Bactrian: scaleBactrian :: SpikeParameter -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Bactrian: slideBactrian :: SpikeParameter -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Bactrian: slideBactrian :: SpikeParameter -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Scale: scale :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Scale: scale :: Shape Double -> Scale Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Scale: scaleContrarily :: Shape -> Scale -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc.Proposal.Scale: scaleContrarily :: Shape Double -> Scale Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc.Proposal.Scale: scaleUnbiased :: Shape -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Scale: scaleUnbiased :: Shape Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Slide: slide :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Slide: slide :: Mean Double -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Proposal.Slide: slideContrarily :: Mean -> StandardDeviation -> PName -> PWeight -> Tune -> Proposal (Double, Double)
+ Mcmc.Proposal.Slide: slideContrarily :: Mean Double -> StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
- Mcmc.Proposal.Slide: slideSymmetric :: StandardDeviation -> PName -> PWeight -> Tune -> Proposal Double
+ Mcmc.Proposal.Slide: slideSymmetric :: StandardDeviation Double -> PName -> PWeight -> Tune -> Proposal Double
- Mcmc.Settings: BurnInWithAutoTuning :: Int -> Int -> BurnInSpecification
+ Mcmc.Settings: BurnInWithAutoTuning :: Int -> Int -> BurnInSettings
- Mcmc.Settings: BurnInWithCustomAutoTuning :: [Int] -> BurnInSpecification
+ Mcmc.Settings: BurnInWithCustomAutoTuning :: [Int] -> BurnInSettings
- Mcmc.Settings: BurnInWithoutAutoTuning :: Int -> BurnInSpecification
+ Mcmc.Settings: BurnInWithoutAutoTuning :: Int -> BurnInSettings
- Mcmc.Settings: NoBurnIn :: BurnInSpecification
+ Mcmc.Settings: NoBurnIn :: BurnInSettings
- Mcmc.Settings: Settings :: AnalysisName -> BurnInSpecification -> Iterations -> ExecutionMode -> ParallelizationMode -> SaveMode -> LogMode -> Verbosity -> Settings
+ Mcmc.Settings: Settings :: AnalysisName -> BurnInSettings -> Iterations -> TraceLength -> ExecutionMode -> ParallelizationMode -> SaveMode -> LogMode -> Verbosity -> Settings
- Mcmc.Settings: [sBurnIn] :: Settings -> BurnInSpecification
+ Mcmc.Settings: [sBurnIn] :: Settings -> BurnInSettings
- Mcmc.Settings: burnInIterations :: BurnInSpecification -> Int
+ Mcmc.Settings: burnInIterations :: BurnInSettings -> Int
- Mcmc.Statistics.Types: type LowerBoundary = Double
+ Mcmc.Statistics.Types: type LowerBoundary a = a
- Mcmc.Statistics.Types: type Mean = Double
+ Mcmc.Statistics.Types: type Mean a = a
- Mcmc.Statistics.Types: type Rate = Double
+ Mcmc.Statistics.Types: type Rate a = a
- Mcmc.Statistics.Types: type Scale = Double
+ Mcmc.Statistics.Types: type Scale a = a
- Mcmc.Statistics.Types: type Shape = Double
+ Mcmc.Statistics.Types: type Shape a = a
- Mcmc.Statistics.Types: type StandardDeviation = Double
+ Mcmc.Statistics.Types: type StandardDeviation a = a
- Mcmc.Statistics.Types: type UpperBoundary = Double
+ Mcmc.Statistics.Types: type UpperBoundary a = a
- Mcmc.Statistics.Types: type Variance = Double
+ Mcmc.Statistics.Types: type Variance a = a

Files

ChangeLog.md view
@@ -5,6 +5,18 @@ ## Unreleased changes  +## 0.6.0.0++-   Improve documentation.+-   Generalized priors allowing automatic differentiation.+-   Hamiltonian proposal.+++### mcmc-tree++-   Moved to another repository: <https://github.com/dschrempf/mcmc-date>.++ ## 0.5.0.0  -   Marginal likelihood estimation using thermodynamic integration or stepping
README.md view
@@ -10,6 +10,7 @@ -   Metropolis-Hastings-Green <sup><a id="fnr.1" class="footref" href="#fn.1">1</a></sup>; -   Metropolis-coupled Markov chain Monte Carlo (also known as parallel     tempering) <sup><a id="fnr.2" class="footref" href="#fn.2">2</a></sup> <sup>, </sup><sup><a id="fnr.3" class="footref" href="#fn.3">3</a></sup>.+-   Hamilton Monte Carlo proposal <sup><a id="fnr.4" class="footref" href="#fn.4">4</a></sup>.   ## Documentation@@ -20,8 +21,8 @@  ## Examples -[Example MCMC analyses](https://github.com/dschrempf/mcmc/tree/master/mcmc-examples) can be built with [Stack](https://docs.haskellstack.org/en/stable/README/) and are attached to this-repository.+[Example MCMC analyses](https://github.com/dschrempf/mcmc/tree/master/mcmc-examples) can be built with [cabal-install](https://cabal.readthedocs.io/en/latest/cabal-commands.html#) or [Stack](https://docs.haskellstack.org/en/stable/README/) and are attached+to this repository.      git clone https://github.com/dschrempf/mcmc.git     cd mcmc@@ -31,7 +32,9 @@      stack exec archery +For a more involved example, have a look at the [phylogenetic dating project](https://github.com/dschrempf/mcmc-dating). + # Footnotes  <sup><a id="fn.1" href="#fnr.1">1</a></sup> Geyer, C. J., Introduction to Markov chain Monte Carlo, In Handbook of@@ -39,8 +42,11 @@  <sup><a id="fn.2" href="#fnr.2">2</a></sup> Geyer, C. J., Markov chain monte carlo maximum likelihood, Computing Science and Statistics, Proceedings of the 23rd Symposium on the Interface,-(), (1991).+(1991).  <sup><a id="fn.3" href="#fnr.3">3</a></sup> Altekar, G., Dwarkadas, S., Huelsenbeck, J. P., & Ronquist, F., Parallel metropolis coupled markov chain monte carlo for bayesian phylogenetic inference, Bioinformatics, 20(3), 407–415 (2004).++<sup><a id="fn.4" href="#fnr.4">4</a></sup> Neal, R. M., Mcmc Using Hamiltonian Dynamics, In S. Brooks, A. Gelman, G.+Jones, & X. Meng (Eds.), Handbook of Markov Chain Monte Carlo (2011). CRC press.
bench/Normal.hs view
@@ -49,12 +49,13 @@           (AnalysisName "Normal")           (BurnInWithAutoTuning 2000 500)           (Iterations 20000)+          TraceAuto           Overwrite           Sequential           NoSave           LogStdOutOnly           Quiet-  a <- mhg noPrior lh cc mon TraceAuto 0 g+  a <- mhg s noPrior lh cc mon 0 g   void $ mcmc s a  ccLarge :: Cycle Double@@ -70,12 +71,13 @@           (AnalysisName "Normal")           (BurnInWithAutoTuning 20 5)           (Iterations 200)+          TraceAuto           Overwrite           Sequential           NoSave           LogStdOutOnly           Quiet-  a <- mhg noPrior lh ccLarge mon TraceAuto 0 g+  a <- mhg s noPrior lh ccLarge mon 0 g   void $ mcmc s a  ccBactrian :: Cycle Double@@ -88,12 +90,13 @@           (AnalysisName "NormalBactrian")           (BurnInWithAutoTuning 2000 200)           (Iterations 20000)+          TraceAuto           Overwrite           Sequential           NoSave           LogStdOutOnly           Quiet-  a <- mhg noPrior lh ccBactrian mon TraceAuto 0 g+  a <- mhg s noPrior lh ccBactrian mon 0 g   void $ mcmc s a  normalMC3 :: GenIO -> Int -> IO ()@@ -103,11 +106,12 @@           (AnalysisName "MC3")           (BurnInWithAutoTuning 200 20)           (Iterations 2000)+          TraceAuto           Overwrite           Sequential           NoSave           LogStdOutOnly           Quiet       mc3S = MC3Settings (NChains n) (SwapPeriod 2) (NSwaps 1)-  a <- mc3 mc3S noPrior lh cc mon TraceAuto 0 g+  a <- mc3 mc3S mcmcS noPrior lh cc mon 0 g   void $ mcmc mcmcS a
bench/Poisson.hs view
@@ -32,7 +32,7 @@     ys = [1976.0 .. 1985.0]     m = sum ys / fromIntegral (length ys) -f :: Int -> Double -> I -> Log Double+f :: Int -> Double -> I -> Likelihood f ft yr (a, b) = poisson l (fromIntegral ft)   where     l = exp $ a + b * yr@@ -71,10 +71,11 @@           (AnalysisName "Poisson")           (BurnInWithAutoTuning 2000 200)           (Iterations 10000)+          TraceAuto           Overwrite           Sequential           NoSave           LogStdOutOnly           Quiet-  a <- mhg noPrior lh proposals mon TraceAuto initial g+  a <- mhg s noPrior lh proposals mon initial g   void $ mcmc s a
mcmc.cabal view
@@ -1,124 +1,139 @@-cabal-version:      2.2-name:               mcmc-version:            0.5.0.0-license:            GPL-3.0-or-later-copyright:          Dominik Schrempf (2021)-maintainer:         dominik.schrempf@gmail.com-author:             Dominik Schrempf-homepage:           https://github.com/dschrempf/mcmc#readme-bug-reports:        https://github.com/dschrempf/mcmc/issues-synopsis:           Sample from a posterior using Markov chain Monte Carlo-description:-    Please see the README on GitHub at <https://github.com/dschrempf/mcmc#readme>+cabal-version:  2.2+name:           mcmc+version:        0.6.0.0+synopsis:       Sample from a posterior using Markov chain Monte Carlo+description:    Please see the README on GitHub at <https://github.com/dschrempf/mcmc#readme>+category:       Math, Statistics+homepage:       https://github.com/dschrempf/mcmc#readme+bug-reports:    https://github.com/dschrempf/mcmc/issues+author:         Dominik Schrempf+maintainer:     dominik.schrempf@gmail.com+copyright:      Dominik Schrempf (2021)+license:        GPL-3.0-or-later+build-type:     Simple -category:           Math, Statistics-build-type:         Simple extra-source-files:     README.md     ChangeLog.md  source-repository head-    type:     git-    location: https://github.com/dschrempf/mcmc+  type: git+  location: https://github.com/dschrempf/mcmc -library-    exposed-modules:-        Mcmc-        Mcmc.Algorithm-        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-        Mcmc.Proposal.Generic-        Mcmc.Proposal.Scale-        Mcmc.Proposal.Slide-        Mcmc.Proposal.Simplex-        Mcmc.Settings-        Mcmc.Statistics.Types+common lib+  ghc-options: -Wall -Wunused-packages+  default-language: Haskell2010 -    hs-source-dirs:   src-    other-modules:-        Mcmc.Internal.ByteString-        Mcmc.Internal.Random-        Mcmc.Internal.Shuffle-        Paths_mcmc+common test+  ghc-options: -Wall -Wunused-packages+  default-language: Haskell2010 -    autogen-modules:  Paths_mcmc-    default-language: Haskell2010-    ghc-options:      -Wall -Wunused-packages-    build-depends:-        aeson >=1.5.6.0,-        base >=4.7 && <5,-        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+common bench+  ghc-options: -Wall -Wunused-packages+  default-language: Haskell2010 -test-suite mcmc-test-    type:             exitcode-stdio-1.0-    main-is:          Spec.hs-    hs-source-dirs:   test-    other-modules:-        Mcmc.ProposalSpec-        Mcmc.SaveSpec-        Paths_mcmc+common exec+  ghc-options: -Wall -Wunused-packages -threaded -rtsopts -with-rtsopts=-N+  default-language: Haskell2010 -    default-language: Haskell2010-    ghc-options:      -Wall -Wunused-packages-    build-depends:-        base >=4.7 && <5,-        hspec >=2.7.10,-        log-domain >=0.13.1,-        mcmc -any,-        mwc-random >=0.15.0.1,-        statistics >=0.15.2.0+library+  import: lib+  exposed-modules:+      Mcmc+      Mcmc.Acceptance+      Mcmc.Algorithm+      Mcmc.Algorithm.MC3+      Mcmc.Algorithm.MHG+      Mcmc.Chain.Chain+      Mcmc.Chain.Link Mcmc.Chain.Save+      Mcmc.Chain.Trace+      Mcmc.Cycle+      Mcmc.Environment+      Mcmc.Internal.Gamma+      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+      Mcmc.Proposal.Generic+      Mcmc.Proposal.Hamiltonian+      Mcmc.Proposal.Scale+      Mcmc.Proposal.Slide+      Mcmc.Proposal.Simplex+      Mcmc.Settings+      Mcmc.Statistics.Types+  other-modules:+      Mcmc.Internal.ByteString+      Mcmc.Internal.Random+      Mcmc.Internal.Shuffle+      Paths_mcmc+  autogen-modules:+      Paths_mcmc+  hs-source-dirs: src+  build-depends:+      aeson+    , base >=4.7 && <5+    , bytestring+    , circular+    , containers+    , data-default+    , deepseq+    , directory+    , dirichlet+    , double-conversion+    , log-domain+    , math-functions+    , matrices+    , microlens+    , mwc-random+    , monad-parallel+    , pretty-show+    , primitive+    , statistics+    , time+    , transformers+    , vector+    , zlib -benchmark mcmc-bench-    type:             exitcode-stdio-1.0-    main-is:          Bench.hs-    hs-source-dirs:   bench-    other-modules:-        Normal-        Poisson-        Paths_mcmc+test-suite mcmc-test+  import: test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Mcmc.ProposalSpec+      Mcmc.SaveSpec+      Paths_mcmc+  hs-source-dirs: test+  build-depends:+      base >=4.7 && <5+    , hspec+    , log-domain+    , mcmc+    , mwc-random+    , statistics -    default-language: Haskell2010-    ghc-options:      -Wall -Wunused-packages-    build-depends:-        base >=4.7 && <5,-        criterion >=1.5.9.0,-        log-domain >=0.13.1,-        mcmc -any,-        microlens >=0.4.12.0,-        mwc-random >=0.15.0.1+benchmark mcmc-bench+  import: bench+  type: exitcode-stdio-1.0+  main-is: Bench.hs+  other-modules:+      Normal+      Poisson+      Paths_mcmc+  autogen-modules:+      Paths_mcmc+  hs-source-dirs: bench+  build-depends:+      base >=4.7 && <5+    , criterion+    , mcmc+    , microlens+    , mwc-random
src/Mcmc.hs view
@@ -134,7 +134,10 @@     slideUniformSymmetric,     slideContrarily,     slideBactrian,+    module Mcmc.Proposal.Hamiltonian,     module Mcmc.Proposal.Simplex,++    -- ** Cycles     Cycle,     cycleFromList,     Order (..),@@ -172,6 +175,7 @@     -- * Prior, likelihood, and posterior values and functions     module Mcmc.Prior,     module Mcmc.Likelihood,+    module Mcmc.Posterior,      -- * MCMC samplers     mcmc,@@ -195,16 +199,18 @@  import Mcmc.Algorithm.MC3 import Mcmc.Algorithm.MHG-import Mcmc.Chain.Chain+import Mcmc.Cycle import Mcmc.Likelihood import Mcmc.MarginalLikelihood import Mcmc.Mcmc import Mcmc.Monitor import Mcmc.Monitor.Parameter import Mcmc.Monitor.ParameterBatch+import Mcmc.Posterior import Mcmc.Prior import Mcmc.Proposal import Mcmc.Proposal.Bactrian+import Mcmc.Proposal.Hamiltonian import Mcmc.Proposal.Scale import Mcmc.Proposal.Simplex import Mcmc.Proposal.Slide
+ src/Mcmc/Acceptance.hs view
@@ -0,0 +1,93 @@+-- |+-- Module      :  Mcmc.Acceptance+-- Description :  Handle acceptance rates+-- Copyright   :  (c) 2021 Dominik Schrempf+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  experimental+-- Portability :  portable+--+-- Creation date: Thu Jul  8 18:12:07 2021.+module Mcmc.Acceptance+  ( -- * Acceptance rates+    AcceptanceRate,+    Acceptance (fromAcceptance),+    emptyA,+    pushA,+    resetA,+    transformKeysA,+    acceptanceRate,+    acceptanceRates,+  )+where++import Control.DeepSeq+import Data.Aeson+import Data.Bifunctor+import Data.Foldable+import qualified Data.Map.Strict as M++-- | Acceptance rate.+type AcceptanceRate = Double++-- | 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, AcceptanceRate)+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 AcceptanceRate)+acceptanceRates =+  M.map+    ( \(as, rs) ->+        if as + rs == 0+          then Nothing+          else Just $ fromIntegral as / fromIntegral (as + rs)+    )+    . fromAcceptance
src/Mcmc/Algorithm.hs view
@@ -33,8 +33,11 @@   -- | Sample the next state.   aIterate :: ParallelizationMode -> a -> IO a -  -- | Auto tune all proposals.-  aAutoTune :: a -> a+  -- | Auto tune all proposals over the last N iterations.+  --+  -- NOTE: Computation in the 'IO' Monad is necessary because the trace is+  -- mutable.+  aAutoTune :: Int -> a -> IO a    -- | Reset acceptance counts.   aResetAcceptance :: a -> a
src/Mcmc/Algorithm/MC3.hs view
@@ -62,17 +62,20 @@ import qualified Data.Vector as V import qualified Data.Vector.Unboxed as U import Data.Word--- import Debug.Trace hiding (trace)+import Mcmc.Acceptance import Mcmc.Algorithm import Mcmc.Algorithm.MHG import Mcmc.Chain.Chain import Mcmc.Chain.Link import Mcmc.Chain.Save import Mcmc.Chain.Trace+import Mcmc.Cycle import Mcmc.Internal.Random import Mcmc.Internal.Shuffle+import Mcmc.Likelihood import Mcmc.Monitor import Mcmc.Posterior+import Mcmc.Prior import Mcmc.Proposal import Mcmc.Settings import Numeric.Log hiding (sum)@@ -275,28 +278,28 @@ -- - The swap period is zero or negative. mc3 ::   MC3Settings ->+  Settings ->   PriorFunction a ->   LikelihoodFunction a ->   Cycle a ->   Monitor a ->-  TraceLength ->   InitialState a ->   GenIO ->   IO (MC3 a)-mc3 s pr lh cc mn tr i0 g+mc3 sMc3 s pr lh cc mn 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 tr i0) gs+    cs <- V.mapM (mhg s pr lh cc mn i0) gs     hcs <- V.izipWithM (initMHG pr lh) (V.convert bs) cs-    return $ MC3 s hcs bs 0 (emptyA [0 .. n - 2]) g+    return $ MC3 sMc3 hcs bs 0 (emptyA [0 .. n - 2]) g   where-    n = fromNChains $ mc3NChains s-    sp = fromSwapPeriod $ mc3SwapPeriod s-    sn = fromNSwaps $ mc3NSwaps s+    n = fromNChains $ mc3NChains sMc3+    sp = fromSwapPeriod $ mc3SwapPeriod sMc3+    sn = fromNSwaps $ mc3NSwaps sMc3     -- NOTE: The initial choice of reciprocal temperatures is based on a few     -- tests but otherwise pretty arbitrary.     --@@ -417,10 +420,10 @@   where     mhgs = mc3MHGChains a --- TODO: Splimix. 'mc3Iterate' is actually not parallel, but concurrent because+-- TODO: Splitmix. 'mc3Iterate' is actually not parallel, but concurrent because -- of the IO constraint. Use pure parallel code when we have a pure generator. ----- However, we to have honor the mutable traces.+-- However, we have to take care of the mutable traces. mc3Iterate ::   ToJSON a =>   ParallelizationMode ->@@ -472,41 +475,40 @@     rNew = (brOld / blOld) ** xi     brNew = blNew * rNew -mc3AutoTune :: ToJSON a => MC3 a -> MC3 a-mc3AutoTune a = a {mc3MHGChains = mhgs'', mc3ReciprocalTemperatures = bs'}-  where-    mhgs = mc3MHGChains a-    -- 1. Auto tune all chains.-    mhgs' = V.map aAutoTune mhgs-    -- 2. Auto tune temperatures.-    optimalRate = getOptimalRate PDimensionUnknown-    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-    -- ratios, which is the same. Also, by working with ratios in (0,1) of-    -- neighboring chains, we ensure the monotonicity of the reciprocal-    -- temperatures.-    ---    -- The factor (1/2) was determined by a few tests and is otherwise-    -- absolutely arbitrary.-    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-    -- the cold chain.-    bs' = foldl' (\xs j -> tuneBeta bs j (xi j) xs) bs [0 .. n - 2]-    coldChain = fromMHG $ V.head mhgs'-    coldPrF = priorFunction coldChain-    coldLhF = likelihoodFunction coldChain-    mhgs'' =-      V.head mhgs'-        `V.cons` V.zipWith-          (setReciprocalTemperature coldPrF coldLhF)-          (V.convert $ U.tail bs')-          (V.tail mhgs')+mc3AutoTune :: ToJSON a => Int -> MC3 a -> IO (MC3 a)+mc3AutoTune l a = do+  -- 1. Auto tune all chains.+  mhgs' <- V.mapM (aAutoTune l) $ mc3MHGChains a+  -- 2. Auto tune temperatures.+  let optimalRate = getOptimalRate PDimensionUnknown+      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+      -- ratios, which is the same. Also, by working with ratios in (0,1) of+      -- neighboring chains, we ensure the monotonicity of the reciprocal+      -- temperatures.+      --+      -- The factor (1/2) was determined by a few tests and is otherwise+      -- absolutely arbitrary.+      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+      -- the cold chain.+      bs' = foldl' (\xs j -> tuneBeta bs j (xi j) xs) bs [0 .. n - 2]+      coldChain = fromMHG $ V.head mhgs'+      coldPrF = priorFunction coldChain+      coldLhF = likelihoodFunction coldChain+      mhgs'' =+        V.head mhgs'+          `V.cons` V.zipWith+            (setReciprocalTemperature coldPrF coldLhF)+            (V.convert $ U.tail bs')+            (V.tail mhgs')+  return $ a {mc3MHGChains = mhgs'', mc3ReciprocalTemperatures = bs'}  mc3ResetAcceptance :: ToJSON a => MC3 a -> MC3 a mc3ResetAcceptance a = a'@@ -536,6 +538,7 @@         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.",
src/Mcmc/Algorithm/MHG.hs view
@@ -33,13 +33,18 @@ import Data.Aeson import qualified Data.ByteString.Lazy.Char8 as BL import Data.Time+import qualified Data.Vector as VB+import Mcmc.Acceptance import Mcmc.Algorithm import Mcmc.Chain.Chain import Mcmc.Chain.Link import Mcmc.Chain.Save import Mcmc.Chain.Trace+import Mcmc.Cycle+import Mcmc.Likelihood import Mcmc.Monitor import Mcmc.Posterior+import Mcmc.Prior hiding (uniform) import Mcmc.Proposal import Mcmc.Settings import Numeric.Log@@ -64,19 +69,20 @@   aCloseMonitors = mhgCloseMonitors   aSave = mhgSave --- NOTE: IO is required because the trace is mutable.- -- | Initialize an MHG algorithm.+--+-- NOTE: Computation in the 'IO' Monad is necessary because the trace is+-- mutable. mhg ::+  Settings ->   PriorFunction a ->   LikelihoodFunction a ->   Cycle a ->   Monitor a ->-  TraceLength ->   InitialState a ->   GenIO ->   IO (MHG a)-mhg pr lh cc mn trLen i0 g = do+mhg s 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@@ -85,10 +91,14 @@     l0 = Link i0 (pr i0) (lh i0)     ac = emptyA $ ccProposals cc     batchMonitorSizes = map getMonitorBatchSize $ mBatches mn-    minimumTraceLength = case trLen of+    minimumTraceLength = case sTraceLength s of       TraceAuto -> 1       TraceMinimum n -> n-    traceLength = maximum $ minimumTraceLength : batchMonitorSizes+    bi = case sBurnIn s of+      BurnInWithAutoTuning _ n -> n+      BurnInWithCustomAutoTuning ns -> maximum ns+      _ -> 0+    traceLength = maximum $ minimumTraceLength : bi : batchMonitorSizes  mhgFn :: AnalysisName -> FilePath mhgFn (AnalysisName nm) = nm ++ ".mcmc.mhg"@@ -210,12 +220,17 @@     t = trace c     n = iteration c +-- Check if the current state is invalid.+--+-- At the moment this just checks whether the prior, likelihood, or posterior+-- are NaN or infinite. mhgIsInValidState :: MHG a -> Bool-mhgIsInValidState a = ((p * l) == 0) || (p * l == (0 / 0))+mhgIsInValidState a = check p || check l || check (p * l)   where     x = link $ fromMHG a     p = prior x     l = likelihood x+    check v = let v' = ln v in isNaN v' || isInfinite v' || v' == 0  -- 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@@ -231,8 +246,10 @@     cc = cycle c     g = generator c -mhgAutoTune :: MHG a -> MHG a-mhgAutoTune (MHG c) = MHG $ c {cycle = autoTuneCycle ac cc}+mhgAutoTune :: Int -> MHG a -> IO (MHG a)+mhgAutoTune n (MHG c) = do+  tr <- VB.map state <$> takeT n (trace c)+  return $ MHG $ c {cycle = autoTuneCycle ac tr cc}   where     ac = acceptance c     cc = cycle c
src/Mcmc/Chain/Chain.hs view
@@ -10,24 +10,51 @@ -- -- Creation date: Tue May  5 18:01:15 2020. module Mcmc.Chain.Chain-  ( PriorFunction,-    noPrior,-    LikelihoodFunction,-    noLikelihood,-    InitialState,+  ( InitialState,     Chain (..),   ) where --- 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.+-- NOTE: Auxiliary data.+--+-- It is not necessary to add another type @b@ to store auxiliary data about the+-- chain. The information can just be stored in @a@ equally well.+--+-- I am not sure if this is the case. If @b@ is affected by a change in @a@, it+-- has to be recomputed. This is difficult to implement at the proposal step.+-- Maybe the new value is not even accepted. On the other hand, one could trust+-- the lazyness of Haskell, and recompute @b@. The computation is done only when+-- the value is accessed.+--+-- That is, I have to think about how to implement auxiliary data A. Prior and+-- likelihood functions can then act on the state space I and A, e.g.,+--+-- > type PriorFunction a b = a -> b -> Log Double+--+-- where a is the type of the state, and b is the auxiliary data type. --- TODO: First class parameters. Make a type class for parameter types (name,--- lens, proposals, monitors).+-- NOTE: First class parameters.+--+-- I thought a lot about implementing a type class for parameters. For example,+-- parameters should have a name, a lens, possibly proposals, monitors, and so+-- on.+--+-- However, this is really difficult. If I use a type class, I need different+-- data types for each parameter which is cumbersome (and slow?). Of course, one+-- could use a data type such as+--+-- > data ParamterSpec a = ParameterSpec { name :: ByteString, pMonitor :: (a -> ByteString) }+--+-- But even in this case we run into problems: There are proposals and monitors+-- acting on a combination of parameters. Even setting the name doesn't make+-- sense in this case.+--+-- I decided to let this idea rest. +import Mcmc.Acceptance import Mcmc.Chain.Link import Mcmc.Chain.Trace+import Mcmc.Cycle import Mcmc.Likelihood import Mcmc.Monitor import Mcmc.Prior
src/Mcmc/Chain/Save.hs view
@@ -27,17 +27,20 @@ import Data.Aeson import Data.Aeson.TH 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+import Mcmc.Acceptance import Mcmc.Chain.Chain import Mcmc.Chain.Link import Mcmc.Chain.Trace+import Mcmc.Cycle import Mcmc.Internal.Random+import Mcmc.Likelihood import Mcmc.Monitor+import Mcmc.Prior import Mcmc.Proposal import Prelude hiding (cycle) @@ -51,7 +54,7 @@     savedTrace :: C.Stack VB.Vector (Link a),     savedAcceptance :: Acceptance Int,     savedSeed :: VU.Vector Word32,-    savedTuningParameters :: [Maybe TuningParameter]+    savedTuningParameters :: [Maybe (TuningParameter, AuxiliaryTuningParameters)]   }   deriving (Eq, Read, Show) @@ -68,7 +71,10 @@   where     ps = ccProposals cc     ac' = transformKeysA ps [0 ..] ac-    ts = [fmap tParam mt | mt <- map prTuner ps]+    ts =+      [ (\t -> (tGetTuningParameter t, tGetAuxiliaryTuningParameters t)) <$> mt+        | mt <- map prTuner ps+      ]  -- | Load a saved chain. --@@ -100,13 +106,9 @@     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-      Nothing -> const 1.0-      Just t -> const t-    cc' =-      tuneCycle-        ( M.map getTuningF $-            M.fromList $-              zip (ccProposals cc) ts-        )-        cc+    tunePs mt p = case mt of+      Nothing -> p+      Just (x, xs) -> either (error . (<> err)) id $ tuneWithTuningParameters x xs p+    err = error "\nfromSavedChain: Proposal with stored tuning parameters is not tunable."+    ps = ccProposals cc+    cc' = cc {ccProposals = zipWith tunePs ts ps}
+ src/Mcmc/Cycle.hs view
@@ -0,0 +1,187 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      :  Mcmc.Cycle+-- Description :  A cycle is a list of proposals+-- Copyright   :  (c) 2021 Dominik Schrempf+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  experimental+-- Portability :  portable+--+-- Creation date: Thu Jul  8 17:56:03 2021.+module Mcmc.Cycle+  ( -- * Cycles+    Order (..),+    Cycle (ccProposals),+    cycleFromList,+    setOrder,+    prepareProposals,+    autoTuneCycle,++    -- * Output+    proposalHLine,+    summarizeCycle,+  )+where++import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Default+import Data.Either+import Data.List+import qualified Data.Map.Strict as M+import qualified Data.Vector as VB+import Mcmc.Acceptance+import Mcmc.Internal.Shuffle+import Mcmc.Proposal+import System.Random.MWC++-- | Define the order in which 'Proposal's are executed in a 'Cycle'. The total+-- number of 'Proposal's per 'Cycle' may differ between 'Order's (e.g., compare+-- 'RandomO' and 'RandomReversibleO').+data Order+  = -- | Shuffle the 'Proposal's in the 'Cycle'. The 'Proposal's are replicated+    -- according to their weights and executed in random order. If a 'Proposal' has+    -- weight @w@, it is executed exactly @w@ times per iteration.+    RandomO+  | -- | The 'Proposal's are executed sequentially, in the order they appear in the+    -- 'Cycle'. 'Proposal's with weight @w>1@ are repeated immediately @w@ times+    -- (and not appended to the end of the list).+    SequentialO+  | -- | Similar to 'RandomO'. However, a reversed copy of the list of+    --  shuffled 'Proposal's is appended such that the resulting Markov chain is+    --  reversible.+    --  Note: the total number of 'Proposal's executed per cycle is twice the number+    --  of 'RandomO'.+    RandomReversibleO+  | -- | Similar to 'SequentialO'. However, a reversed copy of the list of+    -- sequentially ordered 'Proposal's is appended such that the resulting Markov+    -- chain is reversible.+    SequentialReversibleO+  deriving (Eq, Show)++instance Default Order where def = RandomO++-- Describe the order.+describeOrder :: Order -> BL.ByteString+describeOrder RandomO = "The proposals are executed in random order."+describeOrder SequentialO = "The proposals are executed sequentially."+describeOrder RandomReversibleO =+  BL.intercalate+    "\n"+    [ describeOrder RandomO,+      "A reversed copy of the shuffled proposals is appended to ensure reversibility."+    ]+describeOrder SequentialReversibleO =+  BL.intercalate+    "\n"+    [ describeOrder SequentialO,+      "A reversed copy of the sequential proposals is appended to ensure reversibility."+    ]++-- | In brief, a 'Cycle' is a list of proposals.+--+-- The state of the Markov chain will be logged only after all 'Proposal's in+-- the 'Cycle' have been completed, and the iteration counter will be increased+-- by one. The order in which the 'Proposal's are executed is specified by+-- 'Order'. The default is 'RandomO'.+--+-- No proposals with the same name and description are allowed in a 'Cycle', so+-- that they can be uniquely identified.+data Cycle a = Cycle+  { ccProposals :: [Proposal a],+    ccOrder :: Order+  }++-- | Create a 'Cycle' from a list of 'Proposal's.+cycleFromList :: [Proposal a] -> Cycle a+cycleFromList [] =+  error "cycleFromList: Received an empty list but cannot create an empty Cycle."+cycleFromList xs =+  if length uniqueXs == length xs+    then Cycle xs def+    else error $ "\n" ++ msg ++ "cycleFromList: Proposals are not unique."+  where+    uniqueXs = nub xs+    removedXs = xs \\ uniqueXs+    removedNames = map (show . prName) removedXs+    removedDescriptions = map (show . prDescription) removedXs+    removedMsgs = zipWith (\n d -> n ++ " " ++ d) removedNames removedDescriptions+    msg = unlines removedMsgs++-- | Set the order of 'Proposal's in a 'Cycle'.+setOrder :: Order -> Cycle a -> Cycle a+setOrder o c = c {ccOrder = o}++-- | Replicate 'Proposal's according to their weights and possibly shuffle them.+prepareProposals :: Cycle a -> GenIO -> IO [Proposal a]+prepareProposals (Cycle xs o) g = case o of+  RandomO -> shuffle ps g+  SequentialO -> return ps+  RandomReversibleO -> do+    psR <- shuffle ps g+    return $ psR ++ reverse psR+  SequentialReversibleO -> return $ ps ++ reverse ps+  where+    !ps = concat [replicate (fromPWeight $ prWeight p) p | p <- xs]++-- The number of proposals depends on the order.+getNProposalsPerCycle :: Cycle a -> Int+getNProposalsPerCycle (Cycle xs o) = case o of+  RandomO -> once+  SequentialO -> once+  RandomReversibleO -> 2 * once+  SequentialReversibleO -> 2 * once+  where+    once = sum $ map (fromPWeight . prWeight) xs++-- | Calculate acceptance rates and auto tunes the 'Proposal's in the 'Cycle'.+--+-- Do not change 'Proposal's that are not tuneable.+autoTuneCycle :: Acceptance (Proposal a) -> VB.Vector a -> Cycle a -> Cycle a+autoTuneCycle a xs c =+  if sort (M.keys ar) == sort ps+    then c {ccProposals = map tuneF ps}+    else error "autoTuneCycle: Proposals in map and cycle do not match."+  where+    ar = acceptanceRates a+    ps = ccProposals c+    tuneF p = case ar M.!? p of+      Just (Just x) -> fromRight p (tuneWithChainParameters x xs p)+      _ -> p++-- | Horizontal line of proposal summaries.+proposalHLine :: BL.ByteString+proposalHLine = BL.replicate (BL.length proposalHeader) '-'++-- | Summarize the 'Proposal's in the 'Cycle'. Also report acceptance rates.+summarizeCycle :: Acceptance (Proposal a) -> Cycle a -> BL.ByteString+summarizeCycle a c =+  BL.intercalate "\n" $+    [ "Summary of proposal(s) in cycle.",+      nProposalsFullStr,+      describeOrder (ccOrder c),+      proposalHeader,+      proposalHLine+    ]+      ++ [ summarizeProposal+             (prName p)+             (prDescription p)+             (prWeight p)+             (tGetTuningParameter <$> prTuner p)+             (prDimension p)+             (ar p)+           | p <- ps+         ]+      ++ [proposalHLine]+  where+    ps = ccProposals c+    nProposals = getNProposalsPerCycle c+    nProposalsStr = BB.toLazyByteString $ BB.intDec nProposals+    nProposalsFullStr = case nProposals of+      1 -> nProposalsStr <> " proposal is performed per iteration."+      _ -> nProposalsStr <> " proposals are performed per iterations."+    ar m = acceptanceRate m a
src/Mcmc/Environment.hs view
@@ -18,7 +18,7 @@  import Control.Concurrent.MVar import Control.Monad-import Data.Time.Clock+import Data.Time import Mcmc.Logger import Mcmc.Settings import System.IO
+ src/Mcmc/Internal/Gamma.hs view
@@ -0,0 +1,195 @@+-- |+-- Module      :  Mcmc.Internal.Gamma+-- Description :  Generalized gamma function for automatic differentiation+-- Copyright   :  (c) 2021 Dominik Schrempf+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  experimental+-- Portability :  portable+--+-- Creation date: Tue Jul 13 12:53:09 2021.+--+-- The code is taken from "Numeric.SpecFunctions".+module Mcmc.Internal.Gamma+  ( logGammaG,+  )+where++import qualified Data.Vector as VB+import Numeric.Polynomial++mSqrtEps :: RealFloat a => a+mSqrtEps = 1.4901161193847656e-8++mEulerMascheroni :: RealFloat a => a+mEulerMascheroni = 0.5772156649015328606065121++-- | See 'Numeric.SpecFunctions.logGamma'.+logGammaG :: RealFloat a => a -> a+logGammaG z+  | z <= 0 = 1 / 0+  | z < mSqrtEps = log (1 / z - mEulerMascheroni)+  | z < 0.5 = lgamma1_15 z (z - 1) - log z+  | z < 1 = lgamma15_2 z (z - 1) - log z+  | z <= 1.5 = lgamma1_15 (z - 1) (z - 2)+  | z < 2 = lgamma15_2 (z - 1) (z - 2)+  | z < 15 = lgammaSmall z+  | otherwise = lanczosApprox z+{-# SPECIALIZE logGammaG :: Double -> Double #-}++lgamma1_15 :: RealFloat a => a -> a -> a+lgamma1_15 zm1 zm2 =+  r * y + r+    * ( evaluatePolynomial zm1 tableLogGamma_1_15P+          / evaluatePolynomial zm1 tableLogGamma_1_15Q+      )+  where+    r = zm1 * zm2+    y = 0.52815341949462890625++tableLogGamma_1_15P :: RealFloat a => VB.Vector a+tableLogGamma_1_15P =+  VB.fromList+    [ 0.490622454069039543534e-1,+      -0.969117530159521214579e-1,+      -0.414983358359495381969e0,+      -0.406567124211938417342e0,+      -0.158413586390692192217e0,+      -0.240149820648571559892e-1,+      -0.100346687696279557415e-2+    ]+{-# NOINLINE tableLogGamma_1_15P #-}++tableLogGamma_1_15Q :: RealFloat a => VB.Vector a+tableLogGamma_1_15Q =+  VB.fromList+    [ 1,+      0.302349829846463038743e1,+      0.348739585360723852576e1,+      0.191415588274426679201e1,+      0.507137738614363510846e0,+      0.577039722690451849648e-1,+      0.195768102601107189171e-2+    ]+{-# NOINLINE tableLogGamma_1_15Q #-}++lgamma15_2 :: RealFloat a => a -> a -> a+lgamma15_2 zm1 zm2 =+  r * y + r+    * ( evaluatePolynomial (- zm2) tableLogGamma_15_2P+          / evaluatePolynomial (- zm2) tableLogGamma_15_2Q+      )+  where+    r = zm1 * zm2+    y = 0.452017307281494140625++tableLogGamma_15_2P :: RealFloat a => VB.Vector a+tableLogGamma_15_2P =+  VB.fromList+    [ -0.292329721830270012337e-1,+      0.144216267757192309184e0,+      -0.142440390738631274135e0,+      0.542809694055053558157e-1,+      -0.850535976868336437746e-2,+      0.431171342679297331241e-3+    ]+{-# NOINLINE tableLogGamma_15_2P #-}++tableLogGamma_15_2Q :: RealFloat a => VB.Vector a+tableLogGamma_15_2Q =+  VB.fromList+    [ 1,+      -0.150169356054485044494e1,+      0.846973248876495016101e0,+      -0.220095151814995745555e0,+      0.25582797155975869989e-1,+      -0.100666795539143372762e-2,+      -0.827193521891290553639e-6+    ]+{-# NOINLINE tableLogGamma_15_2Q #-}++lgammaSmall :: RealFloat a => a -> a+lgammaSmall = go 0+  where+    go acc z+      | z < 3 = acc + lgamma2_3 z+      | otherwise = go (acc + log zm1) zm1+      where+        zm1 = z - 1++lgamma2_3 :: RealFloat a => a -> a+lgamma2_3 z =+  r * y + r+    * ( evaluatePolynomial zm2 tableLogGamma_2_3P+          / evaluatePolynomial zm2 tableLogGamma_2_3Q+      )+  where+    r = zm2 * (z + 1)+    zm2 = z - 2+    y = 0.158963680267333984375e0++tableLogGamma_2_3P :: RealFloat a => VB.Vector a+tableLogGamma_2_3P =+  VB.fromList+    [ -0.180355685678449379109e-1,+      0.25126649619989678683e-1,+      0.494103151567532234274e-1,+      0.172491608709613993966e-1,+      -0.259453563205438108893e-3,+      -0.541009869215204396339e-3,+      -0.324588649825948492091e-4+    ]+{-# NOINLINE tableLogGamma_2_3P #-}++tableLogGamma_2_3Q :: RealFloat a => VB.Vector a+tableLogGamma_2_3Q =+  VB.fromList+    [ 1,+      0.196202987197795200688e1,+      0.148019669424231326694e1,+      0.541391432071720958364e0,+      0.988504251128010129477e-1,+      0.82130967464889339326e-2,+      0.224936291922115757597e-3,+      -0.223352763208617092964e-6+    ]+{-# NOINLINE tableLogGamma_2_3Q #-}++lanczosApprox :: RealFloat a => a -> a+lanczosApprox z =+  (log (z + g - 0.5) - 1) * (z - 0.5)+    + log (evalRatio tableLanczos z)+  where+    g = 6.024680040776729583740234375++tableLanczos :: RealFloat a => VB.Vector (a, a)+tableLanczos =+  VB.fromList+    [ (56906521.91347156388090791033559122686859, 0),+      (103794043.1163445451906271053616070238554, 39916800),+      (86363131.28813859145546927288977868422342, 120543840),+      (43338889.32467613834773723740590533316085, 150917976),+      (14605578.08768506808414169982791359218571, 105258076),+      (3481712.15498064590882071018964774556468, 45995730),+      (601859.6171681098786670226533699352302507, 13339535),+      (75999.29304014542649875303443598909137092, 2637558),+      (6955.999602515376140356310115515198987526, 357423),+      (449.9445569063168119446858607650988409623, 32670),+      (19.51992788247617482847860966235652136208, 1925),+      (0.5098416655656676188125178644804694509993, 66),+      (0.006061842346248906525783753964555936883222, 1)+    ]+{-# NOINLINE tableLanczos #-}++data L a = L !a !a++evalRatio :: RealFloat a => VB.Vector (a, a) -> a -> a+evalRatio coef x+  | x > 1 = fini $ VB.foldl' stepL (L 0 0) coef+  | otherwise = fini $ VB.foldr' stepR (L 0 0) coef+  where+    fini (L num den) = num / den+    stepR (a, b) (L num den) = L (num * x + a) (den * x + b)+    stepL (L num den) (a, b) = L (num * rx + a) (den * rx + b)+    rx = recip x
src/Mcmc/Likelihood.hs view
@@ -11,7 +11,9 @@ -- Creation date: Wed Mar  3 11:39:04 2021. module Mcmc.Likelihood   ( Likelihood,+    LikelihoodG,     LikelihoodFunction,+    LikelihoodFunctionG,     noLikelihood,   ) where@@ -21,9 +23,16 @@ -- | Likelihood values are stored in log domain. type Likelihood = Log Double +-- | Generalized likelihood.+type LikelihoodG a = Log a+ -- | Likelihood function.-type LikelihoodFunction a = a -> Log Double+type LikelihoodFunction a = LikelihoodFunctionG a Double +-- | Generalized likelihood function.+type LikelihoodFunctionG a b = a -> LikelihoodG b+ -- | Flat likelihood function. Useful for testing and debugging.-noLikelihood :: LikelihoodFunction a+noLikelihood :: RealFloat b => LikelihoodFunctionG a b noLikelihood = const 1.0+{-# SPECIALIZE noLikelihood :: LikelihoodFunction Double #-}
src/Mcmc/Logger.hs view
@@ -42,16 +42,14 @@ import Data.Aeson.TH import qualified Data.ByteString.Lazy.Char8 as BL import Data.Time.Clock+import Data.Version (showVersion) 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+import System.IO  -- | Define where the log output should be directed to.+--+-- Logging is disabled if 'Verbosity' is set to 'Quiet'. data LogMode = LogStdOutAndFile | LogStdOutOnly | LogFileOnly   deriving (Eq, Read, Show) 
src/Mcmc/MarginalLikelihood.hs view
@@ -30,17 +30,19 @@ import qualified Data.Map.Strict as M import qualified Data.Vector as VB import qualified Data.Vector.Unboxed as VU+import Mcmc.Acceptance import Mcmc.Algorithm.MHG import Mcmc.Chain.Chain import Mcmc.Chain.Link import Mcmc.Chain.Trace+import Mcmc.Cycle import Mcmc.Environment import Mcmc.Internal.Random import Mcmc.Likelihood import Mcmc.Logger import Mcmc.Mcmc import Mcmc.Monitor-import Mcmc.Proposal+import Mcmc.Prior import Mcmc.Settings import Numeric.Log hiding (sum) import System.Directory@@ -87,9 +89,9 @@     mlAlgorithm :: MLAlgorithm,     mlNPoints :: NPoints,     -- | Initial burn in at the starting point of the path.-    mlInitialBurnIn :: BurnInSpecification,+    mlInitialBurnIn :: BurnInSettings,     -- | Repetitive burn in at each point on the path.-    mlPointBurnIn :: BurnInSpecification,+    mlPointBurnIn :: BurnInSettings,     -- | The number of iterations performed at each point.     mlIterations :: Iterations,     mlExecutionMode :: ExecutionMode,@@ -235,11 +237,11 @@       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+      ssI = Settings nm biI (Iterations 0) trLen em Sequential NoSave LogFileOnly vb'+      ssP = Settings nm biP is trLen em Sequential NoSave LogFileOnly vb'   logDebugB "mlRun: Initialize MHG algorithm."-  a0 <- liftIO $ mhg prf lhf cc mn trLen i0 g+  a0 <- liftIO $ mhg ssI prf lhf cc mn i0 g   logDebugS $ "mlRun: Perform initial burn in at first point " <> show x0 <> "."   a1 <- sampleAtPoint x0 ssI lhf a0   logDebugB "mlRun: Traverse points."
src/Mcmc/Mcmc.hs view
@@ -21,6 +21,7 @@   ) where +import Control.Exception import Control.Monad import Control.Monad.IO.Class import Control.Monad.Trans.Reader@@ -28,13 +29,13 @@ import Mcmc.Environment import Mcmc.Logger import Mcmc.Settings+import System.Exit import System.IO-import Text.Show.Pretty import Prelude hiding (cycle)  -- The MCMC algorithm has read access to an environment and uses an algorithm -- transforming the state @a@.-type MCMC a = ReaderT (Environment Settings) IO a+type MCMC = ReaderT (Environment Settings) IO  mcmcExecute :: Algorithm a => a -> MCMC a mcmcExecute a = do@@ -52,6 +53,30 @@   logDebugB "Reset acceptance rates."   return $ aResetAcceptance a +mcmcExceptionHandler :: Algorithm a => Environment Settings -> a -> AsyncException -> IO b+mcmcExceptionHandler e a UserInterrupt = do+  putStrLn ""+  putStrLn "USER INTERRUPT!"+  putStrLn "Try to terminate gracefully and save chain for continuation."+  putStrLn "Press CTRL-C again to terminate now."+  putStrLn "Close output files."+  _ <- aCloseMonitors a+  closeEnvironment e+  putStrLn "Try to save settings."+  let s = settings e+  settingsSave s+  putStrLn "Try to save compressed MCMC analysis."+  putStrLn "For long traces, or complex objects, this may take a while."+  let nm = sAnalysisName s+  aSave nm a+  putStrLn "Markov chain saved. Analysis can be continued."+  putStrLn "Terminate gracefully."+  exitWith $ ExitFailure 1+mcmcExceptionHandler _ _ e = throw e++-- XXX: Exception handling. Is it enough to mask execution of monitors and catch+-- UserInterrupt during iterations?+ mcmcExecuteMonitors :: Algorithm a => a -> MCMC () mcmcExecuteMonitors a = do   e <- ask@@ -59,7 +84,8 @@       vb = sVerbosity s       t0 = startingTime e       iTotal = burnInIterations (sBurnIn s) + fromIterations (sIterations s)-  mStdLog <- liftIO (aExecuteMonitors vb t0 iTotal a)+  -- NOTE: Mask asynchronous exceptions when writing monitor files.+  mStdLog <- liftIO $ mask_ $ aExecuteMonitors vb t0 iTotal a   forM_ mStdLog (logOutB "   ")  mcmcIterate :: Algorithm a => Int -> a -> MCMC a@@ -67,8 +93,10 @@   | n < 0 = error "mcmcIterate: Number of iterations is negative."   | n == 0 = return a   | otherwise = do+    e <- ask     p <- sParallelizationMode . settings <$> ask-    a' <- liftIO $ aIterate p a+    -- NOTE: User interrupt is handled during iterations.+    a' <- liftIO $ catch (aIterate p a) (mcmcExceptionHandler e a)     mcmcExecuteMonitors a'     mcmcIterate (n -1) a' @@ -90,11 +118,16 @@ mcmcContinueRun :: Algorithm a => a -> MCMC a mcmcContinueRun a = do   s <- reader settings-  let iTotal = fromIterations (sIterations s) + burnInIterations (sBurnIn s)+  let iBurnIn = burnInIterations (sBurnIn s)+      iNormal = fromIterations (sIterations s)+      iTotal = iBurnIn + iNormal   logInfoB "Continuation of MCMC sampler."   let iCurrent = aIteration a-  logInfoS $ "Current iteration: " ++ show iCurrent ++ "."+  logInfoS $ "Burn in iterations: " ++ show iBurnIn ++ "."+  logInfoS $ "Normal iterations: " ++ show iNormal ++ "."   logInfoS $ "Total iterations: " ++ show iTotal ++ "."+  logInfoS $ "Current iteration: " ++ show iCurrent ++ "."+  when (iCurrent < iBurnIn) $ error "mcmcContinueRun: Can not continue burn in."   let di = iTotal - iCurrent   logInfoB $ aSummarizeCycle a   logInfoS $ "Run chain for " ++ show di ++ " iterations."@@ -122,8 +155,8 @@       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.+          -- Don't add another auto tune period if r == 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."@@ -137,23 +170,23 @@       return a'  -- Auto tune the proposals.-mcmcAutotune :: Algorithm a => a -> MCMC a-mcmcAutotune a = do+mcmcAutotune :: Algorithm a => Int -> a -> MCMC a+mcmcAutotune n a = do   logDebugB "Auto tune."-  return $ aAutoTune a+  liftIO $ aAutoTune n 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'+  a'' <- mcmcAutotune x a'   logInfoB $ aSummarizeCycle a''   logInfoS $ "Acceptance rates calculated over the last " <> show x <> " iterations."   mcmcResetAcceptance a''-mcmcBurnInWithAutoTuning (x:xs) a = do+mcmcBurnInWithAutoTuning (x : xs) a = do   a' <- mcmcIterate x a-  a'' <- mcmcAutotune a'+  a'' <- mcmcAutotune x a'   logDebugB $ aSummarizeCycle a''   logDebugS $ "Acceptance rates calculated over the last " <> show x <> " iterations."   logDebugB $ aStdMonitorHeader a''@@ -200,11 +233,9 @@ -- Initialize the run, execute the run, and close the run. mcmcRun :: Algorithm a => a -> MCMC a mcmcRun a = do+  -- Header.   logInfoHeader--  -- Debug settings.-  logDebugB "The MCMC settings are:"-  reader settings >>= logDebugS . ppShow+  reader settings >>= logInfoB . settingsPrettyPrint    -- Initialize.   a' <- mcmcInitialize a
src/Mcmc/Monitor/Time.hs view
@@ -20,8 +20,7 @@  import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Time.Clock-import Data.Time.Format+import Data.Time import Mcmc.Internal.ByteString  -- | Adapted from System.ProgressBar.renderDuration of package
src/Mcmc/Posterior.hs view
@@ -11,10 +11,22 @@ -- Creation date: Fri May 28 12:26:35 2021. module Mcmc.Posterior   ( Posterior,+    PosteriorG,+    PosteriorFunction,+    PosteriorFunctionG,   ) where  import Numeric.Log  -- | Posterior values are stored in log domain.-type Posterior = Log Double+type Posterior = PosteriorG Double++-- | Generalized posterior.+type PosteriorG a = Log a++-- | Posterior function.+type PosteriorFunction a = PosteriorFunctionG a Double++-- | Generalized posterior function.+type PosteriorFunctionG a b = a -> PosteriorG b
src/Mcmc/Prior.hs view
@@ -13,13 +13,15 @@ -- Creation date: Thu Jul 23 13:26:14 2020. module Mcmc.Prior   ( Prior,+    PriorG,     PriorFunction,-    noPrior,+    PriorFunctionG,      -- * Improper priors-    largerThan,+    noPrior,+    greaterThan,     positive,-    lowerThan,+    lessThan,     negative,      -- * Continuous priors@@ -42,67 +44,88 @@  import Control.Monad import Data.Maybe (fromMaybe)+import Mcmc.Internal.Gamma import Mcmc.Statistics.Types import Numeric.Log import qualified Statistics.Distribution as S-import qualified Statistics.Distribution.Exponential as S-import qualified Statistics.Distribution.Gamma as S-import qualified Statistics.Distribution.Normal as S import qualified Statistics.Distribution.Poisson as S  -- | Prior values are stored in log domain.-type Prior = Log Double+type Prior = PriorG Double +-- | Generalized prior.+type PriorG a = Log a+ -- | Prior function.-type PriorFunction a = a -> Prior+type PriorFunction a = PriorFunctionG a Double +-- | Generalized prior function.+type PriorFunctionG a b = a -> PriorG b+ -- | Flat prior function. Useful for testing and debugging.-noPrior :: PriorFunction a+noPrior :: RealFloat b => PriorFunctionG a b noPrior = const 1.0+{-# SPECIALIZE noPrior :: PriorFunction Double #-} --- | Improper uniform prior; strictly larger than a given value.-largerThan :: LowerBoundary -> PriorFunction Double-largerThan a x-  | x <= a = 0-  | otherwise = 1+-- | Improper uniform prior; strictly greater than a given value.+greaterThan :: RealFloat a => LowerBoundary a -> PriorFunctionG a a+greaterThan a x+  | x > a = 1.0+  | otherwise = 0.0+{-# SPECIALIZE greaterThan :: Double -> PriorFunction Double #-} --- | Improper uniform prior; strictly larger than zero.-positive :: PriorFunction Double-positive = largerThan 0+-- | Improper uniform prior; strictly greater than zero.+positive :: RealFloat a => PriorFunctionG a a+positive = greaterThan 0+{-# SPECIALIZE positive :: PriorFunction Double #-} --- | Improper uniform prior; strictly lower than a given value.-lowerThan :: UpperBoundary -> PriorFunction Double-lowerThan b x-  | x >= b = 0-  | otherwise = 1+-- | Improper uniform prior; strictly less than a given value.+lessThan :: RealFloat a => UpperBoundary a -> PriorFunctionG a a+lessThan a x+  | x < a = 1.0+  | otherwise = 0.0+{-# SPECIALIZE lessThan :: Double -> PriorFunction Double #-} --- | Improper uniform prior; strictly lower than zero.-negative :: PriorFunction Double-negative = lowerThan 0+-- | Improper uniform prior; strictly less than zero.+negative :: RealFloat a => PriorFunctionG a a+negative = lessThan 0+{-# SPECIALIZE negative :: PriorFunction Double #-}  -- | Exponential distributed prior.-exponential :: Rate -> PriorFunction Double-exponential l = Exp . S.logDensity d+--+-- Call 'error' if the rate is zero or negative.+exponential :: RealFloat a => Rate a -> PriorFunctionG a a+exponential l x+  | l <= 0 = error "exponential: Rate is zero or negative."+  | x < 0 = error "exponential: Negative value."+  | otherwise = ll * Exp (negate l * x)   where-    d = S.exponential l+    ll = Exp $ log l+{-# SPECIALIZE exponential :: Double -> PriorFunction Double #-}  -- | Gamma distributed prior.-gamma :: Shape -> Scale -> PriorFunction Double-gamma k t = Exp . S.logDensity d-  where-    d = S.gammaDistr k t+--+-- Call 'error' if the shape or scale are zero or negative.+gamma :: RealFloat a => Shape a -> Scale a -> PriorFunctionG a a+gamma k t x+  | k <= 0 = error "gamma: Shape is zero or negative."+  | t <= 0 = error "gamma: Scale is zero or negative."+  | x < 0 = error "gamma: Negative value."+  | x == 0 = 0.0+  | otherwise = Exp $ log x * (k - 1) - (x / t) - logGammaG k - log t * k+{-# SPECIALIZE gamma :: Double -> Double -> PriorFunction Double #-}  -- | 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+gammaMeanVariance :: RealFloat a => Mean a -> Variance a -> PriorFunctionG a a+gammaMeanVariance m v = gamma k t+  where+    (k, t) = gammaMeanVarianceToShapeScale m v+{-# SPECIALIZE gammaMeanVariance :: Double -> Double -> PriorFunction Double #-}  -- | 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)+gammaMeanOne :: RealFloat a => Shape a -> PriorFunctionG a a+gammaMeanOne k = gamma k (recip k)+{-# SPECIALIZE gammaMeanOne :: Double -> PriorFunction Double #-}  -- The mean and variance of the gamma distribution are --@@ -118,30 +141,51 @@  -- | Calculate mean and variance of the gamma distribution given the shape and -- the scale.-gammaShapeScaleToMeanVariance :: Shape -> Scale -> (Mean, Variance)+gammaShapeScaleToMeanVariance :: Num a => Shape a -> Scale a -> (Mean a, Variance a) gammaShapeScaleToMeanVariance k t = let m = k * t in (m, m * t)+{-# SPECIALIZE gammaShapeScaleToMeanVariance :: Double -> Double -> (Double, Double) #-}  -- | Calculate shape and scale of the gamma distribution given the mean and -- the variance.-gammaMeanVarianceToShapeScale :: Mean -> Variance -> (Shape, Scale)+gammaMeanVarianceToShapeScale :: Fractional a => Mean a -> Variance a -> (Shape a, Scale a) gammaMeanVarianceToShapeScale m v = (m * m / v, v / m)+{-# SPECIALIZE gammaMeanVarianceToShapeScale :: Double -> Double -> (Double, Double) #-} +mLnSqrt2Pi :: RealFloat a => a+mLnSqrt2Pi = 0.9189385332046727417803297364056176398613974736377834128171+{-# INLINE mLnSqrt2Pi #-}+ -- | Normal distributed prior.-normal :: Mean -> StandardDeviation -> PriorFunction Double-normal m s = Exp . S.logDensity d+--+-- Call 'error' if the standard deviation is zero or negative.+normal :: RealFloat a => Mean a -> StandardDeviation a -> PriorFunctionG a a+normal m s x+  | s <= 0 = error "normal: Standard deviation is zero or negative."+  | otherwise = Exp $ (- xm * xm / (2 * s * s)) - denom   where-    d = S.normalDistr m s+    xm = x - m+    denom = mLnSqrt2Pi + log s+{-# SPECIALIZE normal :: Double -> Double -> PriorFunction Double #-}  -- | Uniform prior on [a, b].-uniform :: LowerBoundary -> UpperBoundary -> PriorFunction Double+--+-- Call 'error' if the lower boundary is greather than the upper boundary.+uniform :: RealFloat a => LowerBoundary a -> UpperBoundary a -> PriorFunctionG a a uniform a b x-  | x <= a = 0-  | x >= b = 0-  | otherwise = Exp 0+  | a > b = error "uniform: Lower boundary is greater than upper boundary."+  | x < a = 0.0+  | x > b = 0.0+  | otherwise = 1.0+{-# SPECIALIZE uniform :: Double -> Double -> PriorFunction Double #-}  -- | Poisson distributed prior.-poisson :: Rate -> PriorFunction Int-poisson l = Exp . S.logProbability d+--+-- Call 'error' if the rate is zero or negative.+poisson :: Rate Double -> PriorFunction Int+poisson l n+  | l <= 0 = error "poisson: Rate is zero or negative."+  | n < 0 = error "poisson: Negative value."+  | otherwise = Exp $ S.logProbability d n   where     d = S.poisson l @@ -149,9 +193,11 @@ -- -- Use with care because the elements are checked for positiveness, and this can -- take some time if the list is long and does not contain any zeroes.-product' :: [Log Double] -> Log Double+product' :: RealFloat a => [Log a] -> Log a product' = fromMaybe 0 . prodM+{-# SPECIALIZE product' :: [Log Double] -> Log Double #-}  -- The type could be generalized to any MonadPlus Integer-prodM :: [Log Double] -> Maybe (Log Double)+prodM :: RealFloat a => [Log a] -> Maybe (Log a) prodM = foldM (\ !acc x -> (acc * x) <$ guard (acc /= 0)) 1+{-# SPECIALIZE prodM :: [Log Double] -> Maybe (Log Double) #-}
src/Mcmc/Proposal.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}@@ -31,53 +30,31 @@     ProposalSimple,     Tuner (..),     Tune (..),+    defaultTuningFunction,     createProposal,     TuningParameter,+    AuxiliaryTuningParameters,     tuningParameterMin,     tuningParameterMax,-    tune,+    tuneWithTuningParameters,+    tuneWithChainParameters,     getOptimalRate, -    -- * Cycles-    Order (..),-    Cycle (ccProposals),-    cycleFromList,-    setOrder,-    prepareProposals,-    tuneCycle,-    autoTuneCycle,--    -- * Acceptance rates-    Acceptance (fromAcceptance),-    emptyA,-    pushA,-    resetA,-    transformKeysA,-    acceptanceRate,-    acceptanceRates,-     -- * Output     proposalHeader,-    proposalHLine,     summarizeProposal,-    summarizeCycle,   ) where -import Control.DeepSeq-import Data.Aeson-import Data.Bifunctor import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as BL-import Data.Default import qualified Data.Double.Conversion.ByteString as BC import Data.Function-import Data.List-import qualified Data.Map.Strict as M-import Data.Maybe+import qualified Data.Vector as VB import Lens.Micro+import Lens.Micro.Extras+import Mcmc.Acceptance import Mcmc.Internal.ByteString-import Mcmc.Internal.Shuffle import Numeric.Log hiding (sum) import System.Random.MWC @@ -97,8 +74,9 @@  -- | Check if the weight is positive. pWeight :: Int -> PWeight-pWeight n | n <= 0 = error "pWeight: Proposal weight is zero or negative."-          | otherwise = PWeight n+pWeight n+  | n <= 0 = error "pWeight: Proposal weight is zero or negative."+  | otherwise = PWeight n  -- | Proposal dimension. --@@ -108,8 +86,8 @@ -- high dimensional ones. -- -- Optimal acceptance rates are still subject to controversies. As far as I--- know, research has focused on random walk proposal with a multivariate normal--- distribution of dimension @d@. In this case, the following acceptance rates+-- know, research has focused on random walk proposals with multivariate normal+-- distributions of dimension @d@. In this case, the following acceptance rates -- are desired: -- -- - one dimension: 0.44 (numerical results);@@ -123,19 +101,27 @@ -- Of course, many proposals may not be classical random walk proposals. For -- example, the beta proposal on a simplex ('Mcmc.Proposal.Simplex.beta') -- samples one new variable of the simplex from a beta distribution while--- rescaling all other variables. What is the dimension of this proposal? I--- don't know, but I set the dimension to 2. The reason is that if the dimension--- of the simplex is 2, two variables are changed. If the dimension of the--- simplex is high, one variable is changed substantially, while all others are--- changed marginally.+-- rescaling all other variables. What is the dimension of this proposal? Here,+-- the dimension is set to 2. The reason is that if the dimension of the simplex+-- is 2, two variables are changed. If the dimension of the simplex is high, one+-- variable is changed substantially, while all others are changed marginally. -- -- Further, if a proposal changes a number of variables in the same way (and not--- independently like in a random walk proposal), I still set the dimension of--- the proposal to the number of variables changed.+-- independently like in a random walk proposal), the dimension of the proposal+-- is set to the number of variables changed. ----- Finally, I assume that proposals of unknown dimension have high dimension,--- and use the optimal acceptance rate 0.234.-data PDimension = PDimension Int | PDimensionUnknown+-- Moreover, proposals of unknown dimension are assumed to have high dimension,+-- and the optimal acceptance rate 0.234 is used.+--+-- Finally, special proposals may have completely different desired acceptance+-- rates. For example. the Hamiltonian Monte Carlo proposal (see+-- Mcmc.Proposal.Hamiltonian.hmc) has a desired acceptance rate of 0.65.+-- Specific acceptance rates can be set with 'PSpecial'.+data PDimension+  = PDimension Int+  | PDimensionUnknown+  | -- | Provide dimension ('Int') and desired acceptance rate ('Double').+    PSpecial Int Double  -- | A 'Proposal' is an instruction about how the Markov chain will traverse the -- state space @a@. Essentially, it is a probability mass or probability density@@ -192,20 +178,21 @@  -- | Lift a proposal from one data type to another. ----- Assume the Jacobian is 1.0 (see also 'liftProposal' and 'liftProposalWith').+-- Assume the Jacobian is 1.0. -- -- For example: -- -- @ -- scaleFirstEntryOfTuple = _1 @~ scale -- @+--+-- See also 'liftProposal' and 'liftProposalWith'. infixl 7 @~+ (@~) :: Lens' b a -> Proposal a -> Proposal b (@~) = liftProposal --- | Lift a proposal from one data type to another.------ Assume the Jacobian is 1.0 (see also '(@~)' and 'liftProposalWith').+-- | See '(@~)'. liftProposal :: Lens' b a -> Proposal a -> Proposal b liftProposal = liftProposalWith (const 1.0) @@ -217,7 +204,8 @@ -- 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)+liftProposalWith jf l (Proposal n r d w s t) =+  Proposal n r d w (liftProposalSimple jf l s) (liftTuner jf l <$> t)  -- | Simple proposal without tuning information. --@@ -239,8 +227,9 @@ -- determinant of the Jacobian matrix differs from 1.0. type ProposalSimple a = a -> GenIO -> IO (a, KernelRatio, Jacobian) -convertProposalSimple :: JacobianFunction b -> Lens' b a -> ProposalSimple a -> ProposalSimple b-convertProposalSimple jf l s = s'+-- Lift a simple proposal from one data type to another.+liftProposalSimple :: JacobianFunction b -> Lens' b a -> ProposalSimple a -> ProposalSimple b+liftProposalSimple jf l s = s'   where     s' y g = do       (x', r, j) <- s (y ^. l) g@@ -250,31 +239,72 @@           j' = j * jyx / jxy       return (y', r, j') --- | Tune the acceptance rate of a 'Proposal'; see 'tune', or 'autoTuneCycle'.+-- | Required information to tune 'Proposal's. data Tuner a = Tuner-  { tParam :: TuningParameter,-    tFunc :: TuningParameter -> ProposalSimple a+  { tGetTuningParameter :: TuningParameter,+    -- | Instruction about how to compute new tuning parameter from a given+    -- acceptance rate and the old tuning parameter.+    tComputeTuningParameter :: AcceptanceRate -> TuningParameter -> TuningParameter,+    tGetAuxiliaryTuningParameters :: AuxiliaryTuningParameters,+    -- | Instruction about how to compute new auxiliary tuning parameters from a+    -- given trace and the old auxiliary tuning parameters.+    tComputeAuxiliaryTuningParameters ::+      VB.Vector a ->+      AuxiliaryTuningParameters ->+      AuxiliaryTuningParameters,+    -- | Given the tuning parameter, and the auxiliary tuning parameters, get+    -- the tuned simple proposal.+    --+    -- Should return 'Left' if the vector of auxiliary tuning parameters is+    -- invalid.+    tGetSimpleProposal ::+      TuningParameter ->+      AuxiliaryTuningParameters ->+      Either String (ProposalSimple a)   } -convertTuner :: JacobianFunction b -> Lens' b a -> Tuner a -> Tuner b-convertTuner jf l (Tuner p f) = Tuner p f'+-- Lift tuner from one data type to another.+liftTuner :: JacobianFunction b -> Lens' b a -> Tuner a -> Tuner b+liftTuner jf l (Tuner p fP ps fPs g) = Tuner p fP ps fPs' g'   where-    f' x = convertProposalSimple jf l $ f x+    fPs' = fPs . VB.map (view l)+    g' x xs = liftProposalSimple jf l <$> g x xs --- | Tune the proposal?+-- | Tune proposal? data Tune = Tune | NoTune   deriving (Show, Eq)  -- | Tuning parameter.+--+--  The larger the tuning parameter, the larger the proposal and the lower the+-- expected acceptance rate; and vice versa. type TuningParameter = Double --- | Create a tuneable proposal.+-- | Auxiliary tuning parameters; vector may be empty.+type AuxiliaryTuningParameters = VB.Vector TuningParameter++-- | Default tuning function.+--+-- Subject to change.+defaultTuningFunction ::+  -- Optimal acceptance rate.+  PDimension ->+  AcceptanceRate ->+  TuningParameter ->+  TuningParameter+defaultTuningFunction d r t = let rO = getOptimalRate d in exp (2 * (r - rO)) * t++noAuxiliaryTuningFunction :: VB.Vector a -> AuxiliaryTuningParameters -> AuxiliaryTuningParameters+noAuxiliaryTuningFunction _ ts = ts++-- | Create a proposal with a single tuning parameter.+--+-- Proposals with arbitrary tuning parameters have to be created manually. See+-- 'Tuner' for more information, and 'Mcmc.Proposal.Hamiltonian' for an example. 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.+  -- | Function creating a simple proposal for a given tuning parameter.   (TuningParameter -> ProposalSimple a) ->   -- | Dimension.   PDimension ->@@ -285,17 +315,30 @@   -- | Activate tuning?   Tune ->   Proposal a-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+createProposal r f d n w Tune =+  Proposal n r d w (f 1.0) (Just tuner)+  where+    fT = defaultTuningFunction d+    fTs = noAuxiliaryTuningFunction+    g t _ = Right $ f t+    tuner = Tuner 1.0 fT VB.empty fTs g+createProposal r f d n w NoTune =+  Proposal n r d w (f 1.0) Nothing --- | Minimal tuning parameter; @1e-12@, subject to change.+-- IDEA: Per proposal type tuning parameter boundaries. For example, a sliding+-- proposal with a large tuning parameter is not a problem. But then, if the+-- tuning parameters are very different from one, a different base proposal+-- should be chosen.++-- | Minimal tuning parameter; @1e-5@, subject to change. -- -- >>> tuningParameterMin -- 1e-5 tuningParameterMin :: TuningParameter tuningParameterMin = 1e-5 --- | Maximal tuning parameter; @1e12@, subject to change.+-- | Maximal tuning parameter; @1e3@, subject to change.+-- -- >>> tuningParameterMax -- 1e3 tuningParameterMax :: TuningParameter@@ -303,20 +346,42 @@  -- | Tune a 'Proposal'. ----- The size of the proposal is proportional to the tuning parameter which has a--- positive lower bound of 'tuningParameterMin'.+-- The size of the proposal is proportional to the tuning parameter which has+-- positive lower and upper boundaries of 'tuningParameterMin' and+-- 'tuningParameterMax', respectively. ----- The tuning function maps the current tuning parameter to a new one.+-- Auxiliary tuning parameters may also be used by the 'Tuner' of the proposal. ----- Return 'Nothing' if 'Proposal' is not tuneable.-tune :: (TuningParameter -> TuningParameter) -> Proposal a -> Maybe (Proposal a)-tune f m = do-  (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}+-- Return 'Left' if:+--+-- - the 'Proposal' is not tuneable;+--+-- - the auxiliary tuning parameters are invalid.+tuneWithTuningParameters ::+  TuningParameter ->+  AuxiliaryTuningParameters ->+  Proposal a ->+  Either String (Proposal a)+tuneWithTuningParameters t ts p = case prTuner p of+  Nothing -> Left "tuneWithTuningParameters: Proposal is not tunable."+  Just (Tuner _ fT _ fTs g) ->+    -- Ensure that the tuning parameter is strictly positive and well bounded.+    let t' = max tuningParameterMin t+        t'' = min tuningParameterMax t'+        psE = g t'' ts+     in case psE of+          Left err -> Left $ "tune: " <> err+          Right ps -> Right $ p {prSimple = ps, prTuner = Just $ Tuner t'' fT ts fTs g} +-- | See 'tuneWithTuningParameters' and 'Tuner'.+tuneWithChainParameters :: AcceptanceRate -> VB.Vector a -> Proposal a -> Either String (Proposal a)+tuneWithChainParameters ar xs p = case prTuner p of+  Nothing -> Left "tuneWithChainParameters: Proposal is not tunable."+  Just (Tuner t fT ts fTs _) ->+    let t' = fT ar t+        ts' = fTs xs ts+     in tuneWithTuningParameters t' ts' p+ -- | See 'PDimension'. getOptimalRate :: PDimension -> Double getOptimalRate (PDimension n)@@ -329,6 +394,7 @@   | n >= 5 = 0.234   | otherwise = error "getOptimalRate: Proposal dimension is not an integer?" getOptimalRate PDimensionUnknown = 0.234+getOptimalRate (PSpecial _ r) = r  -- Warn if acceptance rate is lower. rateMin :: Double@@ -338,122 +404,6 @@ rateMax :: Double rateMax = 0.9 --- | Define the order in which 'Proposal's are executed in a 'Cycle'. The total--- number of 'Proposal's per 'Cycle' may differ between 'Order's (e.g., compare--- 'RandomO' and 'RandomReversibleO').-data Order-  = -- | Shuffle the 'Proposal's in the 'Cycle'. The 'Proposal's are replicated-    -- according to their weights and executed in random order. If a 'Proposal' has-    -- weight @w@, it is executed exactly @w@ times per iteration.-    RandomO-  | -- | The 'Proposal's are executed sequentially, in the order they appear in the-    -- 'Cycle'. 'Proposal's with weight @w>1@ are repeated immediately @w@ times-    -- (and not appended to the end of the list).-    SequentialO-  | -- | Similar to 'RandomO'. However, a reversed copy of the list of-    --  shuffled 'Proposal's is appended such that the resulting Markov chain is-    --  reversible.-    --  Note: the total number of 'Proposal's executed per cycle is twice the number-    --  of 'RandomO'.-    RandomReversibleO-  | -- | Similar to 'SequentialO'. However, a reversed copy of the list of-    -- sequentially ordered 'Proposal's is appended such that the resulting Markov-    -- chain is reversible.-    SequentialReversibleO-  deriving (Eq, Show)--instance Default Order where def = RandomO---- Describe the order.-describeOrder :: Order -> BL.ByteString-describeOrder RandomO = "The proposals are executed in random order."-describeOrder SequentialO = "The proposals are executed sequentially."-describeOrder RandomReversibleO =-  BL.intercalate-    "\n"-    [ describeOrder RandomO,-      "A reversed copy of the shuffled proposals is appended to ensure reversibility."-    ]-describeOrder SequentialReversibleO =-  BL.intercalate-    "\n"-    [ describeOrder SequentialO,-      "A reversed copy of the sequential proposals is appended to ensure reversibility."-    ]---- | In brief, a 'Cycle' is a list of proposals.------ The state of the Markov chain will be logged only after all 'Proposal's in--- the 'Cycle' have been completed, and the iteration counter will be increased--- by one. The order in which the 'Proposal's are executed is specified by--- 'Order'. The default is 'RandomO'.------ No proposals with the same name and description are allowed in a 'Cycle', so--- that they can be uniquely identified.-data Cycle a = Cycle-  { ccProposals :: [Proposal a],-    ccOrder :: Order-  }---- | Create a 'Cycle' from a list of 'Proposal's.-cycleFromList :: [Proposal a] -> Cycle a-cycleFromList [] =-  error "cycleFromList: Received an empty list but cannot create an empty Cycle."-cycleFromList xs =-  if length (nub xs) == length xs-    then Cycle xs def-    else error "cycleFromList: Proposals are not unique."---- | Set the order of 'Proposal's in a 'Cycle'.-setOrder :: Order -> Cycle a -> Cycle a-setOrder o c = c {ccOrder = o}---- | Replicate 'Proposal's according to their weights and possibly shuffle them.-prepareProposals :: Cycle a -> GenIO -> IO [Proposal a]-prepareProposals (Cycle xs o) g = case o of-  RandomO -> shuffle ps g-  SequentialO -> return ps-  RandomReversibleO -> do-    psR <- shuffle ps g-    return $ psR ++ reverse psR-  SequentialReversibleO -> return $ ps ++ reverse ps-  where-    !ps = concat [replicate (fromPWeight $ prWeight p) p | p <- xs]---- The number of proposals depends on the order.-getNProposalsPerCycle :: Cycle a -> Int-getNProposalsPerCycle (Cycle xs o) = case o of-  RandomO -> once-  SequentialO -> once-  RandomReversibleO -> 2 * once-  SequentialReversibleO -> 2 * once-  where-    once = sum $ map (fromPWeight . prWeight) xs---- | Tune 'Proposal's in the 'Cycle'. See 'tune'.-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}-    else error "tuneCycle: Propoals in map and cycle do not match."-  where-    ps = ccProposals c-    tuneF p = case m M.!? p of-      Nothing -> p-      Just f -> fromMaybe p (tune f p)---- | Calculate acceptance rates and auto tune the 'Proposal's in the 'Cycle'. For--- now, a 'Proposal' is enlarged when the acceptance rate is above 0.44, and--- shrunk otherwise. Do not change 'Proposal's that are not tuneable.-autoTuneCycle :: Acceptance (Proposal a) -> Cycle a -> Cycle a-autoTuneCycle a = tuneCycle (M.mapWithKey tuningF $ acceptanceRates a)-  where-    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 ->   BL.ByteString ->@@ -477,67 +427,6 @@     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 =@@ -552,10 +441,6 @@     "Tuning parameter"     "Consider manual adjustment" --- | Horizontal line of proposal summaries.-proposalHLine :: BL.ByteString-proposalHLine = BL.replicate (BL.length proposalHeader) '-'- -- | Proposal summary. summarizeProposal ::   PName ->@@ -582,7 +467,7 @@     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) tuningParameter+    tuneParamStr = BL.fromStrict $ maybe "" (BC.toFixed 4) tuningParameter     checkRate rate       | rate < rateMin = Just "rate too low"       | rate > rateMax = Just "rate too high"@@ -599,32 +484,3 @@             (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-summarizeCycle a c =-  BL.intercalate "\n" $-    [ "Summary of proposal(s) in cycle.",-      nProposalsFullStr,-      describeOrder (ccOrder c),-      proposalHeader,-      proposalHLine-    ]-      ++ [ summarizeProposal-             (prName p)-             (prDescription p)-             (prWeight p)-             (tParam <$> prTuner p)-             (prDimension p)-             (ar p)-           | p <- ps-         ]-      ++ [proposalHLine]-  where-    ps = ccProposals c-    nProposals = getNProposalsPerCycle c-    nProposalsStr = BB.toLazyByteString $ BB.intDec nProposals-    nProposalsFullStr = case nProposals of-      1 -> nProposalsStr <> " proposal is performed per iteration."-      _ -> nProposalsStr <> " proposals are performed per iterations."-    ar m = acceptanceRate m a
src/Mcmc/Proposal/Bactrian.hs view
@@ -31,7 +31,7 @@  genBactrian ::   SpikeParameter ->-  StandardDeviation ->+  StandardDeviation Double ->   GenIO ->   IO Double genBactrian m s g = do@@ -42,7 +42,7 @@   b <- bernoulli 0.5 g   return $ if b then x else - x -logDensityBactrian :: SpikeParameter -> StandardDeviation  -> Double -> Log Double+logDensityBactrian :: SpikeParameter -> StandardDeviation Double -> Double -> Log Double logDensityBactrian m s x = Exp $ log $ kernel1 + kernel2   where     mn = m * s@@ -54,7 +54,7 @@  bactrianAdditive ::   SpikeParameter ->-  StandardDeviation  ->+  StandardDeviation Double ->   ProposalSimple Double bactrianAdditive m s x g = do   dx <- genBactrian m s g@@ -63,8 +63,8 @@ -- bactrianSimple lens spike stdDev tune forwardOp backwardOp bactrianAdditiveSimple ::   SpikeParameter ->-  StandardDeviation  ->-  TuningParameter  ->+  StandardDeviation Double ->+  TuningParameter ->   ProposalSimple Double bactrianAdditiveSimple m s t   | m < 0 = error "bactrianAdditiveSimple: Spike parameter negative."@@ -85,7 +85,7 @@ -- See https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3845170/. slideBactrian ::   SpikeParameter ->-  StandardDeviation ->+  StandardDeviation Double ->   PName ->   PWeight ->   Tune ->@@ -105,7 +105,7 @@  bactrianMult ::   SpikeParameter ->-  StandardDeviation  ->+  StandardDeviation Double ->   ProposalSimple Double bactrianMult m s x g = do   du <- genBactrian m s g@@ -115,7 +115,11 @@       jac = Exp $ log $ recip u   return (x * u, qYX / qXY, jac) -bactrianMultSimple :: SpikeParameter -> StandardDeviation  -> TuningParameter  -> ProposalSimple Double+bactrianMultSimple ::+  SpikeParameter ->+  StandardDeviation Double ->+  TuningParameter ->+  ProposalSimple Double bactrianMultSimple m s t   | m < 0 = error "bactrianMultSimple: Spike parameter negative."   | m >= 1 = error "bactrianMultSimple: Spike parameter 1.0 or larger."@@ -123,10 +127,12 @@   | otherwise = bactrianMult m (t * s)  -- | Multiplicative proposal with kernel similar to the silhouette of a Bactrian--- camel. See 'slideBactrian'.+-- camel.+--+-- See 'Mcmc.Proposal.Scale.scale', and 'slideBactrian'. scaleBactrian ::   SpikeParameter ->-  StandardDeviation ->+  StandardDeviation Double ->   PName ->   PWeight ->   Tune ->
src/Mcmc/Proposal/Generic.hs view
@@ -19,17 +19,36 @@ import Numeric.Log import Statistics.Distribution --- | Generic function to create proposals for continuous parameters ('Double').+-- | Generic function to create proposals for continuous parameters (e.g.,+-- 'Double').+--+-- The procedure is as follows: Let \(\mathbb{X}\) be the state space and \(x\)+-- be the current state.+--+-- 1. Let \(D\) be a continuous probability distribution on \(\mathbb{D}\);+--    sample an auxiliary variable \(epsilon \sim D\).+--+-- 2. Suppose \(\odot : \mathbb{X} \times \mathbb{D} \to \mathbb{X}\). Propose a+--    new state \(x' = x \odot \epsilon\).+--+-- 3. If the proposal is unbiased, the Metropolis-Hastings-Green ratio can+--    directly be calculated using the posterior function.+--+-- 4. However, if the proposal is biased: Suppose \(g : \mathbb{D} \to+--    \mathbb{D}\) inverses the auxiliary variable \(\epsilon\) such that \(x =+--    x' \odot g(\epsilon)\). Calculate the Metropolis-Hastings-Green ratio+--    using the posterior function, \(g\), \(D\), \(\epsilon\), and possibly a+--    Jacobian function. genericContinuous ::   (ContDistr d, ContGen d) =>   -- | Probability distribution   d ->-  -- | Forward operator.+  -- | Forward operator \(\odot\).   --   -- For example, for a multiplicative proposal on one variable the forward   -- operator is @(*)@, so that @x * u = y@.   (a -> Double -> a) ->-  -- | Inverse operator.+  -- | Inverse operator \(g\) of the auxiliary variable.   --   -- For example, 'recip' for a multiplicative proposal on one variable, since   -- @y * (recip u) = x * u * (recip u) = x@.@@ -46,8 +65,8 @@   -- That is, the determinant of the Jacobian matrix of multiplication is just   -- the reciprocal value of @u@ (with conversion to log domain).   ---  -- Required for proposals for which absolute value of the determinant of the-  -- Jacobian differs from 1.0.+  -- Required for proposals for which the absolute value of the determinant of+  -- the Jacobian differs from 1.0.   --   -- Conversion to log domain is necessary, because some determinants of   -- Jacobians are very small (or large).@@ -67,15 +86,22 @@   return (x `f` u, r, j) {-# INLINEABLE genericContinuous #-} --- | Generic function to create proposals for discrete parameters ('Int').+-- | Generic function to create proposals for discrete parameters (e.g., 'Int').+--+-- See 'genericContinuous'. genericDiscrete ::   (DiscreteDistr d, DiscreteGen d) =>   -- | Probability distribution.   d ->-  -- | Forward operator, e.g. (+), so that x + dx = x'.+  -- | Forward operator.+  --+  -- For example, (+), so that x + dx = x'.   (a -> Int -> a) ->-  -- | Inverse operator, e.g., 'negate', so that x' + (negate dx) = x. Only-  -- required for biased proposals.+  -- | Inverse operator \(g\) of the auxiliary variable.+  --+  -- For example, 'negate', so that x' + (negate dx) = x.+  --+  -- Only required for biased proposals.   Maybe (Int -> Int) ->   ProposalSimple a genericDiscrete d f mfInv x g = do
+ src/Mcmc/Proposal/Hamiltonian.hs view
@@ -0,0 +1,448 @@+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Module      :  Mcmc.Proposal.Hamiltonian+-- Description :  Hamiltonian Monte Carlo proposal+-- Copyright   :  (c) 2021 Dominik Schrempf+-- License     :  GPL-3.0-or-later+--+-- Maintainer  :  dominik.schrempf@gmail.com+-- Stability   :  experimental+-- Portability :  portable+--+-- Creation date: Mon Jul  5 12:59:42 2021.+--+-- The Hamiltonian Monte Carlo (HMC) proposal.+--+-- For references, see:+--+-- - [1] Chapter 5 of Handbook of Monte Carlo: Neal, R. M., MCMC Using+--   Hamiltonian Dynamics, In S. Brooks, A. Gelman, G. Jones, & X. Meng (Eds.),+--   Handbook of Markov Chain Monte Carlo (2011), CRC press.+--+-- - [2] Gelman, A., Carlin, J. B., Stern, H. S., & Rubin, D. B., Bayesian data+--   analysis (2014), CRC Press.+--+-- - [3] Review by Betancourt and notes: Betancourt, M., A conceptual+--   introduction to Hamiltonian Monte Carlo, arXiv, 1701–02434 (2017).+--+-- NOTE on implementation:+--+-- - The implementation assumes the existence of the gradient. Like so, the user+--   can use automatic or manual differentiation, depending on the problem at+--   hand.+--+-- - The state needs to be list like or 'Traversable' so that the structure of+--   the state space is available. A 'Traversable' constraint on the data type+--   is nice because it is more general than, for example, a list, and+--   user-defined data structures can be used.+--+-- - The state needs to have a zip-like 'Applicative' instance so that+-- - matrix/vector operations can be performed.++module Mcmc.Proposal.Hamiltonian+  ( Gradient,+    Masses,+    LeapfrogTrajectoryLength,+    LeapfrogScalingFactor,+    HTune (..),+    HSettings (..),+    hamiltonian,+  )+where++import Data.Foldable+import qualified Data.Matrix as M+import Data.Maybe+import Data.Traversable+import qualified Data.Vector as VB+import Mcmc.Prior+import Mcmc.Proposal+import Numeric.Log+import Statistics.Distribution+import Statistics.Distribution.Normal+import qualified Statistics.Function as S+import qualified Statistics.Sample as S+import System.Random.MWC++-- TODO: At the moment, the HMC proposal is agnostic of the prior and+-- likelihood, that is, the posterior function. This means, that it cannot know+-- when it reaches a point with zero posterior probability. This also affects+-- restricted or constrained parameters. See Gelman p. 303.++-- TODO: No-U-turn sampler.++-- TODO: Riemannian adaptation.++-- | Gradient of the log posterior function.+type Gradient f = f Double -> f Double++-- | Function validating the state.+--+-- Useful if parameters are constrained.+type Validate f = f Double -> Bool++-- | Masses of parameters.+--+-- NOTE: Full specification of a mass matrix including off-diagonal elements is+-- not supported.+--+-- NOTE: Parameters without masses ('Nothing') are not changed by the+-- Hamiltonian proposal.+--+-- The masses roughly describe how reluctant the particle moves through the+-- state space. If a parameter has higher mass, the momentum in this direction+-- will be changed less by the provided gradient, than when the same parameter+-- has lower mass.+--+-- The proposal is more efficient if masses are assigned according to the+-- inverse (co)-variance structure of the posterior function. That is,+-- parameters changing on larger scales should have lower masses than parameters+-- changing on lower scales. In particular, and for a diagonal mass matrix, the+-- optimal masses are the inverted variances of the parameters distributed+-- according to the posterior function.+--+-- Of course, the scales of the parameters of the posterior function are usually+-- unknown. Often, it is sufficient to+--+-- - set the masses to identical values roughly scaled with the inverted+--   estimated average variance of the posterior function; or even to+--+-- - set all masses to 1.0, and trust the tuning algorithm (see+--   'HTuneMassesAndLeapfrog') to find the correct values.+type Masses f = f (Maybe Double)++-- | Mean leapfrog trajectory length \(L\).+--+-- Number of leapfrog steps per proposal.+--+-- To avoid problems with ergodicity, the actual number of leapfrog steps is+-- sampled proposal from a discrete uniform distribution over the interval+-- \([\text{floor}(0.8L),\text{ceiling}(1.2L)]\).+--+-- For a discussion of ergodicity and reasons why randomization is important,+-- see [1] p. 15; also mentioned in [2] p. 304.+--+-- NOTE: To avoid errors, the left bound has an additional hard minimum of 1,+-- and the right bound is required to be larger equal than the left bound.+--+-- Usually set to 10, but larger values may be desirable.+type LeapfrogTrajectoryLength = Int++-- | Mean of leapfrog scaling factor \(\epsilon\).+--+-- Determines the size of each leapfrog step.+--+-- To avoid problems with ergodicity, the actual leapfrog scaling factor is+-- sampled per proposal from a continuous uniform distribution over the interval+-- \((0.8\epsilon,1.2\epsilon]\).+--+-- For a discussion of ergodicity and reasons why randomization is important,+-- see [1] p. 15; also mentioned in [2] p. 304.+--+-- Usually set such that \( L \epsilon = 1.0 \), but smaller values may be+-- required if acceptance rates are low.+type LeapfrogScalingFactor = Double++-- Target state containing parameters.+type Positions f = f Double++-- Momenta of the parameters.+type Momenta f = f (Maybe Double)++-- | Tuning settings.+--+--+-- Tuning of leapfrog parameters:+--+-- We expect that the larger the leapfrog step size the larger the proposal step+-- size and the lower the acceptance ratio. Consequently, if the acceptance rate+-- is too low, the leapfrog step size is decreased and vice versa. Further, the+-- leapfrog trajectory length is scaled such that the product of the leapfrog+-- step size and trajectory length stays constant.+--+-- Tuning of masses:+--+-- The variances of all parameters of the posterior distribution obtained over+-- the last auto tuning interval is calculated and the masses are amended using+-- the old masses and the inverted variances. If, for a specific coordinate, the+-- sample size is too low, or if the calculated variance is out of predefined+-- bounds, the mass of the affected position is not changed.+data HTune+  = -- | Tune masses and leapfrog parameters.+    HTuneMassesAndLeapfrog+  | -- | Tune leapfrog parameters only.+    HTuneLeapfrogOnly+  | -- | Do not tune at all.+    HNoTune+  deriving (Eq, Show)++-- | Specifications for Hamilton Monte Carlo proposal.+data HSettings f = HSettings+  { hGradient :: Gradient f,+    hMaybeValidate :: Maybe (Validate f),+    hMasses :: Masses f,+    hLeapfrogTrajectoryLength :: LeapfrogTrajectoryLength,+    hLeapfrogScalingFactor :: LeapfrogScalingFactor,+    hTune :: HTune+  }++checkHSettings :: Foldable f => HSettings f -> Maybe String+checkHSettings (HSettings _ _ masses l eps _)+  | any f masses = Just "checkHSettings: One or more masses are zero or negative."+  | l < 1 = Just "checkHSettings: Leapfrog trajectory length is zero or negative."+  | eps <= 0 = Just "checkHSettings: Leapfrog scaling factor is zero or negative."+  | otherwise = Nothing+  where+    f (Just m) = m <= 0+    f Nothing = False++generateMomenta ::+  Traversable f =>+  Masses f ->+  GenIO ->+  IO (Momenta f)+generateMomenta masses gen = traverse (generateWith gen) masses+  where+    generateWith g (Just m) = let d = normalDistr 0 (sqrt m) in Just <$> genContVar d g+    generateWith _ Nothing = pure Nothing++priorMomenta ::+  (Applicative f, Foldable f) =>+  Masses f ->+  Momenta f ->+  Prior+priorMomenta masses phi = foldl' (*) 1.0 $ f <$> masses <*> phi+  where+    f (Just m) (Just p) = let d = normalDistr 0 (sqrt m) in Exp $ logDensity d p+    f Nothing Nothing = 1.0+    f _ _ = error "priorMomenta: Got just a mass and no momentum or the other way around."++leapfrog ::+  Applicative f =>+  Gradient f ->+  Maybe (Validate f) ->+  Masses f ->+  LeapfrogTrajectoryLength ->+  LeapfrogScalingFactor ->+  Positions f ->+  Momenta f ->+  -- Maybe (Positions', Momenta').+  Maybe (Positions f, Momenta f)+leapfrog grad mVal masses l eps theta phi = do+  let -- The first half step of the momenta.+      phiHalf = leapfrogStepMomenta 0.5 eps grad theta phi+  -- L-1 full steps. This gives the positions theta_{L-1}, and the momenta+  -- phi_{L-1/2}.+  (thetaLM1, phiLM1Half) <- go (l - 1) (Just (theta, phiHalf))+  -- The last full step of the positions.+  thetaL <- valF $ leapfrogStepPositions eps masses thetaLM1 phiLM1Half+  let -- The last half step of the momenta.+      phiL = leapfrogStepMomenta 0.5 eps grad thetaL phiLM1Half+  return (thetaL, phiL)+  where+    valF x = case mVal of+      Nothing -> Just x+      Just f -> if f x then Just x else Nothing+    go _ Nothing = Nothing+    go 0 (Just (t, p)) = Just (t, p)+    go n (Just (t, p)) =+      let t' = leapfrogStepPositions eps masses t p+          p' = leapfrogStepMomenta 1.0 eps grad t' p+          r = (,p') <$> valF t'+       in go (n - 1) r++leapfrogStepMomenta ::+  Applicative f =>+  -- Size of step (half or full step).+  Double ->+  LeapfrogScalingFactor ->+  Gradient f ->+  -- Current positions.+  Positions f ->+  -- Current momenta.+  Momenta f ->+  -- New momenta.+  Momenta f+leapfrogStepMomenta xi eps grad theta phi = phi <+. ((xi * eps) .* grad theta)+  where+    (<+.) :: Applicative f => f (Maybe Double) -> f Double -> f (Maybe Double)+    (<+.) xs ys = f <$> xs <*> ys+    f Nothing _ = Nothing+    f (Just x) y = Just $ x + y++leapfrogStepPositions ::+  Applicative f =>+  LeapfrogScalingFactor ->+  Masses f ->+  -- Current positions.+  Positions f ->+  -- Current momenta.+  Momenta f ->+  Positions f+-- The arguments are flipped to encounter the maybe momentum.+leapfrogStepPositions eps masses theta phi = theta <+. (mScaledReversed .*> phi)+  where+    (<+.) :: Applicative f => f Double -> f (Maybe Double) -> f Double+    (<+.) xs ys = f <$> xs <*> ys+    f x Nothing = x+    f x (Just y) = x + y+    mScaledReversed = (fmap . fmap) ((* eps) . (** (-1))) masses+    (.*>) :: Applicative f => f (Maybe Double) -> f (Maybe Double) -> f (Maybe Double)+    (.*>) xs ys = g <$> xs <*> ys+    g (Just x) (Just y) = Just $ x * y+    g Nothing Nothing = Nothing+    g _ _ = error "leapfrogStepPositions: Got just a mass and no momentum or the other way around."++-- Scalar-vector multiplication.+(.*) :: Applicative f => Double -> f Double -> f Double+(.*) x ys = (* x) <$> ys++-- NOTE: Fixed parameters without mass have a tuning parameter of NaN.+massesToTuningParameters :: Foldable f => Masses f -> AuxiliaryTuningParameters+massesToTuningParameters = VB.fromList . map (fromMaybe nan) . toList+  where+    nan = 0 / 0++-- We need the structure in order to fill it with the given parameters.+tuningParametersToMasses ::+  Traversable f =>+  AuxiliaryTuningParameters ->+  Masses f ->+  Either String (Masses f)+tuningParametersToMasses xs ms =+  if null xs'+    then sequenceA msE+    else Left "tuningParametersToMasses: Too many values."+  where+    (xs', msE) = mapAccumL setValue (VB.toList xs) ms+    setValue [] _ = ([], Left "tuningParametersToMasses: Too few values.")+    -- NOTE: Recover fixed parameters and unset their mass.+    setValue (y : ys) _ = let y' = if isNaN y then Nothing else Just y in (ys, Right y')++hTuningParametersToSettings ::+  Traversable f =>+  TuningParameter ->+  AuxiliaryTuningParameters ->+  HSettings f ->+  Either String (HSettings f)+hTuningParametersToSettings t ts (HSettings g v m l e tn) =+  if tn == HTuneMassesAndLeapfrog+    then case tuningParametersToMasses ts m of+      Left err -> Left err+      Right m' -> Right $ HSettings g v m' lTuned eTuned tn+    else Right $ HSettings g v m lTuned eTuned tn+  where+    -- The larger epsilon, the larger the proposal step size and the lower the+    -- expected acceptance ratio.+    --+    -- Further, we roughly keep \( L * \epsilon = 1.0 \). The equation is not+    -- correct, because we pull L closer to the original value to keep the+    -- runtime somewhat acceptable.+    lTuned = ceiling $ fromIntegral l / (t ** 0.9) :: Int+    eTuned = t * e++hamiltonianSimpleWithTuningParameters ::+  (Applicative f, Traversable f) =>+  HSettings f ->+  TuningParameter ->+  AuxiliaryTuningParameters ->+  Either String (ProposalSimple (Positions f))+hamiltonianSimpleWithTuningParameters s t ts = case hTuningParametersToSettings t ts s of+  Left err -> Left err+  Right s' -> Right $ hamiltonianSimple s'++hamiltonianSimple ::+  (Applicative f, Traversable f) =>+  HSettings f ->+  ProposalSimple (Positions f)+hamiltonianSimple (HSettings gradient mVal masses l e _) theta g = do+  phi <- generateMomenta masses g+  lRan <- uniformR (lL, lR) g+  eRan <- uniformR (eL, eR) g+  case leapfrog gradient mVal masses lRan eRan theta phi of+    Nothing -> return (theta, 0.0, 1.0)+    Just (theta', phi') ->+      let prPhi = priorMomenta masses phi+          -- NOTE: Neal page 12: In order for the proposal to be in detailed+          -- balance, the momenta have to be negated before proposing the new value.+          -- This is not required here since the prior involves normal distributions+          -- centered around 0. However, if the multivariate normal distribution is+          -- used, it makes a difference.+          prPhi' = priorMomenta masses phi'+          kernelR = prPhi' / prPhi+       in return (theta', kernelR, 1.0)+  where+    lL = maximum [1 :: Int, floor $ (0.8 :: Double) * fromIntegral l]+    lR = maximum [lL, ceiling $ (1.2 :: Double) * fromIntegral l]+    eL = 0.8 * e+    eR = 1.2 * e++minVariance :: Double+minVariance = 1e-6++maxVariance :: Double+maxVariance = 1e6++minSamples :: Int+minSamples = 60++computeAuxiliaryTuningParameters ::+  Foldable f =>+  VB.Vector (Positions f) ->+  AuxiliaryTuningParameters ->+  AuxiliaryTuningParameters+computeAuxiliaryTuningParameters xss ts =+  VB.zipWith (\t -> rescueWith t . calcSamplesAndVariance) ts xssT+  where+    -- TODO: Improve matrix transposition.+    xssT = VB.fromList $ M.toColumns $ M.fromLists $ VB.toList $ VB.map toList xss+    calcSamplesAndVariance xs = (VB.length $ VB.uniq $ S.gsort xs, S.variance xs)+    rescueWith t (sampleSize, var) =+      if var < minVariance || maxVariance < var || sampleSize < minSamples+        then -- then traceShow ("Rescue with " <> show t) t+          t+        else+          let t' = sqrt (t * recip var)+           in -- in traceShow ("Old mass " <> show t <> " new mass " <> show t') t'+              t'++-- | Hamiltonian Monte Carlo proposal.+--+-- The 'Applicative' and 'Traversable' instances are used for element-wise+-- operations.+--+-- Assume a zip-like 'Applicative' instance so that cardinality remains+-- constant.+--+-- NOTE: The desired acceptance rate is 0.65, although the dimension of the+-- proposal is high.+--+-- NOTE: The speed of this proposal can change drastically when tuned because+-- the leapfrog trajectory length is changed.+hamiltonian ::+  (Applicative f, Traversable f) =>+  -- | The sample state is used to calculate the dimension of the proposal.+  f Double ->+  HSettings f ->+  PName ->+  PWeight ->+  Proposal (f Double)+hamiltonian x s n w = case checkHSettings s of+  Just err -> error err+  Nothing ->+    let desc = PDescription "Hamiltonian Monte Carlo (HMC)"+        dim = PSpecial (length x) 0.65+        ts = massesToTuningParameters (hMasses s)+        ps = hamiltonianSimple s+        p' = Proposal n desc dim w ps+        fT = defaultTuningFunction dim+        tS = hTune s+        fTs =+          if tS == HTuneMassesAndLeapfrog+            then computeAuxiliaryTuningParameters+            else \_ xs -> xs+     in case tS of+          HNoTune -> p' Nothing+          _ -> p' $ Just $ Tuner 1.0 fT ts fTs (hamiltonianSimpleWithTuningParameters s)
src/Mcmc/Proposal/Scale.hs view
@@ -26,7 +26,7 @@  -- The actual proposal with tuning parameter. The tuning parameter does not -- change the mean.-scaleSimple :: Shape -> Scale -> TuningParameter -> ProposalSimple Double+scaleSimple :: Shape Double -> Scale Double -> TuningParameter -> ProposalSimple Double scaleSimple k th t =   genericContinuous     (gammaDistr (k / t) (th * t))@@ -36,10 +36,18 @@   where     jac _ = Exp . log . recip --- | Multiplicative proposal with gamma distributed kernel.+-- | Multiplicative proposal.+--+-- The gamma distribution is used to sample the multiplier. Therefore, this and+-- all derived proposals are log-additive in that they do not change the sign of+-- the state. Further, the value zero is never proposed when having a strictly+-- positive value.+--+-- Consider using 'Mcmc.Proposal.Slide.slide' to allow proposition of values+-- having opposite sign. scale ::-  Shape ->-  Scale ->+  Shape Double ->+  Scale Double ->   PName ->   PWeight ->   Tune ->@@ -48,12 +56,12 @@   where     description = PDescription $ "Scale; shape: " ++ show k ++ ", scale: " ++ show th --- | Multiplicative proposal with gamma distributed kernel.+-- | See 'scale'. -- -- The scale of the gamma distribution is set to (shape)^{-1}, so that the mean -- of the gamma distribution is 1.0. scaleUnbiased ::-  Shape ->+  Shape Double ->   PName ->   PWeight ->   Tune ->@@ -62,7 +70,11 @@   where     description = PDescription $ "Scale unbiased; shape: " ++ show k -scaleContrarilySimple :: Shape -> Scale -> TuningParameter -> ProposalSimple (Double, Double)+scaleContrarilySimple ::+  Shape Double ->+  Scale Double ->+  TuningParameter ->+  ProposalSimple (Double, Double) scaleContrarilySimple k th t =   genericContinuous     (gammaDistr (k / t) (th * t))@@ -73,13 +85,13 @@     contra (x, y) u = (x * u, y / u)     jac _ u = Exp $ log $ recip $ u * u --- | Multiplicative proposal with gamma distributed kernel.+-- | See 'scale'. -- -- The two values are scaled contrarily so that their product stays constant. -- Contrary proposals are useful when parameters are confounded. scaleContrarily ::-  Shape ->-  Scale ->+  Shape Double ->+  Scale Double ->   PName ->   PWeight ->   Tune ->
src/Mcmc/Proposal/Slide.hs view
@@ -26,14 +26,16 @@ import Statistics.Distribution.Uniform  -- The actual proposal with tuning parameter.-slideSimple :: Mean -> StandardDeviation -> TuningParameter -> ProposalSimple Double+slideSimple :: Mean Double -> StandardDeviation Double -> TuningParameter -> ProposalSimple Double slideSimple m s t =   genericContinuous (normalDistr m (s * t)) (+) (Just negate) Nothing --- | Additive proposal with normally distributed kernel.+-- | Additive proposal.+--+-- A normal distribution is used to sample the addend. slide ::-  Mean ->-  StandardDeviation ->+  Mean Double ->+  StandardDeviation Double ->   PName ->   PWeight ->   Tune ->@@ -43,15 +45,17 @@     description = PDescription $ "Slide; mean: " ++ show m ++ ", sd: " ++ show s  -- The actual proposal with tuning parameter.-slideSymmetricSimple :: StandardDeviation -> TuningParameter -> ProposalSimple Double+slideSymmetricSimple :: StandardDeviation Double -> TuningParameter -> ProposalSimple Double slideSymmetricSimple s t =   genericContinuous (normalDistr 0.0 (s * t)) (+) Nothing Nothing --- | Additive proposal with normally distributed kernel with mean zero. This--- proposal is very fast, because the Metropolis-Hastings-Green ratio does not--- include calculation of the forwards and backwards kernels.+-- | See 'slide'.+--+-- Use a normal distribution with mean zero. This proposal is fast, because the+-- Metropolis-Hastings-Green ratio does not include calculation of the forwards+-- and backwards kernels. slideSymmetric ::-  StandardDeviation ->+  StandardDeviation Double ->   PName ->   PWeight ->   Tune ->@@ -65,9 +69,11 @@ slideUniformSimple d t =   genericContinuous (uniformDistr (- t * d) (t * d)) (+) Nothing Nothing --- | Additive proposal with uniformly distributed kernel with mean zero. This--- proposal is very fast, because the Metropolis-Hastings-Green ratio does not--- include calculation of the forwards and backwards kernels.+-- | See 'slide'.+--+-- Use a uniformly distributed kernel with mean zero. This proposal is fast,+-- because the Metropolis-Hastings-Green ratio does not include calculation of+-- the forwards and backwards kernels. slideUniformSymmetric ::   Size ->   PName ->@@ -82,20 +88,22 @@ contra (x, y) u = (x + u, y - u)  slideContrarilySimple ::-  Mean ->-  StandardDeviation ->+  Mean Double ->+  StandardDeviation Double ->   TuningParameter ->   ProposalSimple (Double, Double) slideContrarilySimple m s t =   genericContinuous (normalDistr m (s * t)) contra (Just negate) Nothing --- | Additive proposal with normally distributed kernel.+-- | See 'slide'. --+-- Use a normally distributed kernel.+-- -- The two values are slid contrarily so that their sum stays constant. Contrary -- proposals are useful when parameters are confounded. slideContrarily ::-  Mean ->-  StandardDeviation ->+  Mean Double ->+  StandardDeviation Double ->   PName ->   PWeight ->   Tune ->
src/Mcmc/Settings.hs view
@@ -17,7 +17,7 @@   ( -- * Data types     AnalysisName (..),     HasAnalysisName (..),-    BurnInSpecification (..),+    BurnInSettings (..),     burnInIterations,     Iterations (..),     TraceLength (..),@@ -34,16 +34,21 @@     settingsSave,     settingsLoad,     settingsCheck,+    settingsPrettyPrint,   ) where  import Data.Aeson import Data.Aeson.TH+import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy.Char8 as BL import Mcmc.Logger import System.Directory import System.IO +bsInt :: Int -> BL.ByteString+bsInt = BB.toLazyByteString . BB.intDec+ -- | Analysis name of the MCMC sampler. newtype AnalysisName = AnalysisName {fromAnalysisName :: String}   deriving (Eq, Read, Show)@@ -56,7 +61,7 @@   getAnalysisName :: s -> AnalysisName  -- | Burn in specification.-data BurnInSpecification+data BurnInSettings   = -- | No burn in.     NoBurnIn   | -- | Burn in for a given number of iterations.@@ -75,17 +80,27 @@     BurnInWithCustomAutoTuning [Int]   deriving (Eq, Read, Show) -$(deriveJSON defaultOptions ''BurnInSpecification)+$(deriveJSON defaultOptions ''BurnInSettings) --- Check if the burn in specification is valid.-burnInValid :: BurnInSpecification -> Bool+burnInPrettyPrint :: BurnInSettings -> BL.ByteString+burnInPrettyPrint NoBurnIn =+  "None."+burnInPrettyPrint (BurnInWithoutAutoTuning x) =+  bsInt x <> " iterations; no auto tune."+burnInPrettyPrint (BurnInWithAutoTuning x y) =+  bsInt x <> " iterations; auto tune with a period of " <> bsInt y <> "."+burnInPrettyPrint (BurnInWithCustomAutoTuning xs) =+  bsInt (sum xs) <> " iterations; custom auto tune periods."++-- Check if the burn in settings are valid.+burnInValid :: BurnInSettings -> 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 :: BurnInSettings -> Int burnInIterations NoBurnIn = 0 burnInIterations (BurnInWithoutAutoTuning n) = n burnInIterations (BurnInWithAutoTuning n _) = n@@ -103,13 +118,28 @@ -- -- 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.+  = -- | Automatically determine the minimum length of the trace. The value is+    -- the maximum of used+    --+    -- - 'Mcmc.Monitor.MonitorBatch' sizes+    --+    -- - auto tune intervals during burn in     TraceAuto   | -- | Store a given minimum number of iterations of the chain. Store more     --  iterations if required (see 'TraceAuto').     TraceMinimum Int+  deriving (Eq, Show) +$(deriveJSON defaultOptions ''TraceLength)++traceLengthPrettyPrint :: TraceLength -> BL.ByteString+traceLengthPrettyPrint TraceAuto = "Determined automatically."+traceLengthPrettyPrint (TraceMinimum x) = "Minimum length of " <> bsInt x <> "."++validTraceLength :: TraceLength -> Bool+validTraceLength (TraceMinimum n) = n > 0+validTraceLength _ = True+ -- | Execution mode. data ExecutionMode   = -- | Perform new run.@@ -132,6 +162,11 @@ class HasExecutionMode s where   getExecutionMode :: s -> ExecutionMode +executionModePrettyPrint :: ExecutionMode -> BL.ByteString+executionModePrettyPrint Fail = "Fail if output files exist."+executionModePrettyPrint Overwrite = "Overwrite existing output files."+executionModePrettyPrint Continue = "Expect output files exist."+ -- | Open a file honoring the execution mode. -- -- Call 'error' if execution mode is@@ -194,11 +229,16 @@  $(deriveJSON defaultOptions ''SaveMode) +saveModePrettyPrint :: SaveMode -> BL.ByteString+saveModePrettyPrint NoSave = "Do not save analysis."+saveModePrettyPrint Save = "Save analysis."+ -- | Settings of an MCMC sampler. data Settings = Settings   { sAnalysisName :: AnalysisName,-    sBurnIn :: BurnInSpecification,+    sBurnIn :: BurnInSettings,     sIterations :: Iterations,+    sTraceLength :: TraceLength,     sExecutionMode :: ExecutionMode,     sParallelizationMode :: ParallelizationMode,     sSaveMode :: SaveMode,@@ -270,13 +310,14 @@   -- | Current iteration.   Int ->   IO ()-settingsCheck s@(Settings nm bi i em _ _ _ _) iCurrent+settingsCheck s@(Settings nm bi i tl em _ _ _ _) iCurrent   | null (fromAnalysisName nm) = serr "Analysis name is the empty string."   | burnInIterations bi < 0 = serr "Number of burn in iterations is negative."-  | not $ burnInValid bi = serr $ "Burn in setting invalid: " <> show bi+  | 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."+  | not $ validTraceLength tl = serr $ "Trace length invalid: " <> show tl <> "."   | iCurrent /= 0 && em /= Continue =     serr "Current iteration is non-zero but execution mode is not 'Continue'."   | iCurrent == 0 && em == Continue =@@ -284,3 +325,24 @@   | otherwise = return ()   where     serr = settingsError s iCurrent++logModePrettyPrint :: LogMode -> BL.ByteString+logModePrettyPrint LogStdOutAndFile = "Log to standard output and file."+logModePrettyPrint LogStdOutOnly = "Log to standard output only."+logModePrettyPrint LogFileOnly = "Log to file only."++-- | Pretty print settings.+settingsPrettyPrint :: Settings -> BL.ByteString+settingsPrettyPrint (Settings nm bi is tl em pm sm lm vb) =+  BL.unlines+    [ "The MCMC settings are:",+      "  Analysis name:        " <> BL.pack (fromAnalysisName nm) <> ".",+      "  Burn in:              " <> burnInPrettyPrint bi,+      "  Iterations:           " <> bsInt (fromIterations is) <> " iterations.",+      "  Trace length:         " <> traceLengthPrettyPrint tl,+      "  Execution mode:       " <> executionModePrettyPrint em,+      "  Parallelization mode: " <> BL.pack (show pm) <> ".",+      "  Save mode:            " <> saveModePrettyPrint sm,+      "  Log mode:             " <> logModePrettyPrint lm,+      "  Verbosity:            " <> BL.pack (show vb) <> "."+    ]
src/Mcmc/Statistics/Types.hs view
@@ -24,22 +24,22 @@ where  -- | Mean of a distribution.-type Mean = Double+type Mean a = a  -- | Standard deviation of a distribution.-type StandardDeviation = Double+type StandardDeviation a = a  -- | Variance of a distribution.-type Variance = Double+type Variance a = a  -- | Shape of a distribution.-type Shape = Double+type Shape a = a  -- | Scale of a distribution.-type Scale = Double+type Scale a = a  -- | Rate of a distribution.-type Rate = Double+type Rate a = a  -- | Dimension of a distribution. type Dimension = Int@@ -50,7 +50,7 @@ type Size = Double  -- | Lower boundary of a distribution.-type LowerBoundary = Double+type LowerBoundary a = a  -- | Upper boundary of a distribution.-type UpperBoundary = Double+type UpperBoundary a = a
test/Mcmc/ProposalSpec.hs view
@@ -14,6 +14,7 @@   ) where +import Mcmc.Cycle import Mcmc.Proposal import Mcmc.Proposal.Slide import System.Random.MWC
test/Mcmc/SaveSpec.hs view
@@ -47,23 +47,27 @@ mon :: Monitor Double mon = Monitor monStd [] [] +settings :: Settings+settings =+  Settings+    (AnalysisName "SaveSpec")+    (BurnInWithAutoTuning 20 10)+    (Iterations 100)+    TraceAuto+    Overwrite+    Sequential+    NoSave+    LogStdOutOnly+    Quiet+ spec :: Spec spec = do   describe "save and load" $     it "doesn't change the MCMC chain" $       do         gen <- R.create-        let s =-              Settings-                (AnalysisName "SaveSpec")-                (BurnInWithAutoTuning 20 10)-                (Iterations 200)-                Overwrite-                Sequential-                NoSave-                LogStdOutOnly-                Quiet-        c <- fromMHG <$> mhg noPrior lh proposals mon TraceAuto 0 gen+        a <- mhg settings noPrior lh proposals mon 0 gen+        c <- fromMHG <$> mcmc settings a         savedChain <- toSavedChain c         c' <- fromSavedChain noPrior lh proposals mon savedChain         putStrLn "@load . save@ should be @id@."@@ -76,8 +80,8 @@         -- g1' <- R.save $ generator c'         -- g1 `shouldBe` g1'         putStrLn "Sampling from the chains should be the same."-        r <- fromMHG <$> mcmc s (MHG c)-        r' <- fromMHG <$> mcmc s (MHG c')+        r <- fromMHG <$> mcmcContinue (Iterations 100) settings (MHG c)+        r' <- fromMHG <$> mcmcContinue (Iterations 100) settings (MHG c')         link r `shouldBe` link r'         iteration r `shouldBe` iteration r'         frozenT2 <- freezeT (trace c)@@ -87,20 +91,22 @@         g2' <- R.save $ generator r'         g2 `shouldBe` g2' --- -- TODO: 'mhContinue'.--- describe "mhContinue"---   $ it "mh 200 + mhContinue 200 == mh 400"---   $ do---     gen1 <- create---     let s1 = chain "SaveSpec" (const 1) likelihood proposals mon 0 nBurn nAutoTune 400 gen1---     r1 <- mh s1---     gen2 <- create---     let s2 = chain "SaveSpec" (const 1) likelihood proposals mon 0 nBurn nAutoTune 200 gen2---     r2' <- mh s2---     r2 <- mhContinue 200 r2'---     link r1 `shouldBe` link r2---     iteration r1 `shouldBe` iteration r2---     trace r1 `shouldBe` trace r2---     g <- save $ generator r1---     g' <- save $ generator r2---     g `shouldBe` g'+  describe "mhContinue" $+    it "mcmc 50 + mcmcContinue 50 == mcmc 100" $+      do+        gen1 <- R.create+        a1 <- mhg settings noPrior lh proposals mon 0 gen1+        r1 <- fromMHG <$> mcmc settings a1+        gen2 <- R.create+        let settings' = settings {sIterations = Iterations 50}+        a2 <- mhg settings' noPrior lh proposals mon 0 gen2+        r2' <- mcmc settings' a2+        r2 <- fromMHG <$> mcmcContinue (Iterations 50) settings' r2'+        link r1 `shouldBe` link r2+        iteration r1 `shouldBe` iteration r2+        frozenT1 <- freezeT (trace r1)+        frozenT2 <- freezeT (trace r2)+        frozenT1 `shouldBe` frozenT2+        g <- R.save $ generator r1+        g' <- R.save $ generator r2+        g `shouldBe` g'