{-# LANGUAGE RankNTypes #-}

-- |
-- Module      :  Mcmc.Proposal.Scale
-- Description :  Multiplicative proposals
-- Copyright   :  2021 Dominik Schrempf
-- License     :  GPL-3.0-or-later
--
-- Maintainer  :  dominik.schrempf@gmail.com
-- Stability   :  unstable
-- Portability :  portable
--
-- Creation date: Thu May 14 21:49:23 2020.
module Mcmc.Proposal.Scale
  ( scale,
    scaleUnbiased,
    scaleContrarily,
  )
where

import Mcmc.Proposal
import Mcmc.Proposal.Generic
import Mcmc.Statistics.Types
import Numeric.Log
import Statistics.Distribution.Gamma

-- The actual proposal with tuning parameter. The tuning parameter does not
-- change the mean.
scalePFunction :: Shape Double -> Scale Double -> TuningParameter -> PFunction Double
scalePFunction :: Double -> Double -> Double -> PFunction Double
scalePFunction Double
k Double
th Double
t =
  forall d a.
(ContDistr d, ContGen d) =>
d
-> (a -> Double -> a)
-> Maybe (Double -> Double)
-> Maybe (a -> Double -> Jacobian)
-> PFunction a
genericContinuous
    (Double -> Double -> GammaDistribution
gammaDistr (Double
k forall a. Fractional a => a -> a -> a
/ Double
t) (Double
th forall a. Num a => a -> a -> a
* Double
t))
    forall a. Num a => a -> a -> a
(*)
    (forall a. a -> Maybe a
Just forall a. Fractional a => a -> a
recip)
    (forall a. a -> Maybe a
Just forall {b} {p}. Floating b => p -> b -> Log b
jac)
  where
    jac :: p -> b -> Log b
jac p
_ = forall a. a -> Log a
Exp forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Floating a => a -> a
log forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Fractional a => a -> a
recip

-- | 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 Double ->
  Scale Double ->
  PName ->
  PWeight ->
  Tune ->
  Proposal Double
scale :: Double -> Double -> PName -> PWeight -> Tune -> Proposal Double
scale Double
k Double
th = forall a.
PDescription
-> (Double -> PFunction a)
-> PSpeed
-> PDimension
-> PName
-> PWeight
-> Tune
-> Proposal a
createProposal PDescription
description (Double -> Double -> Double -> PFunction Double
scalePFunction Double
k Double
th) PSpeed
PFast (Int -> PDimension
PDimension Int
1)
  where
    description :: PDescription
description = String -> PDescription
PDescription forall a b. (a -> b) -> a -> b
$ String
"Scale; shape: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Double
k forall a. [a] -> [a] -> [a]
++ String
", scale: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Double
th

-- | 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 Double ->
  PName ->
  PWeight ->
  Tune ->
  Proposal Double
scaleUnbiased :: Double -> PName -> PWeight -> Tune -> Proposal Double
scaleUnbiased Double
k = forall a.
PDescription
-> (Double -> PFunction a)
-> PSpeed
-> PDimension
-> PName
-> PWeight
-> Tune
-> Proposal a
createProposal PDescription
description (Double -> Double -> Double -> PFunction Double
scalePFunction Double
k (Double
1 forall a. Fractional a => a -> a -> a
/ Double
k)) PSpeed
PFast (Int -> PDimension
PDimension Int
1)
  where
    description :: PDescription
description = String -> PDescription
PDescription forall a b. (a -> b) -> a -> b
$ String
"Scale unbiased; shape: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Double
k

scaleContrarilyPFunction ::
  Shape Double ->
  Scale Double ->
  TuningParameter ->
  PFunction (Double, Double)
scaleContrarilyPFunction :: Double -> Double -> Double -> PFunction (Double, Double)
scaleContrarilyPFunction Double
k Double
th Double
t =
  forall d a.
(ContDistr d, ContGen d) =>
d
-> (a -> Double -> a)
-> Maybe (Double -> Double)
-> Maybe (a -> Double -> Jacobian)
-> PFunction a
genericContinuous
    (Double -> Double -> GammaDistribution
gammaDistr (Double
k forall a. Fractional a => a -> a -> a
/ Double
t) (Double
th forall a. Num a => a -> a -> a
* Double
t))
    forall {b}. Fractional b => (b, b) -> b -> (b, b)
contra
    (forall a. a -> Maybe a
Just forall a. Fractional a => a -> a
recip)
    (forall a. a -> Maybe a
Just forall {b} {p}. Floating b => p -> b -> Log b
jac)
  where
    contra :: (b, b) -> b -> (b, b)
contra (b
x, b
y) b
u = (b
x forall a. Num a => a -> a -> a
* b
u, b
y forall a. Fractional a => a -> a -> a
/ b
u)
    jac :: p -> a -> Log a
jac p
_ a
u = forall a. a -> Log a
Exp forall a b. (a -> b) -> a -> b
$ forall a. Floating a => a -> a
log forall a b. (a -> b) -> a -> b
$ forall a. Fractional a => a -> a
recip forall a b. (a -> b) -> a -> b
$ a
u forall a. Num a => a -> a -> a
* a
u

-- | See 'scale'.
--
-- The two values are scaled contrarily so that their product stays constant.
-- Contrary proposals are useful when parameters are confounded.
scaleContrarily ::
  Shape Double ->
  Scale Double ->
  PName ->
  PWeight ->
  Tune ->
  Proposal (Double, Double)
scaleContrarily :: Double
-> Double -> PName -> PWeight -> Tune -> Proposal (Double, Double)
scaleContrarily Double
k Double
th = forall a.
PDescription
-> (Double -> PFunction a)
-> PSpeed
-> PDimension
-> PName
-> PWeight
-> Tune
-> Proposal a
createProposal PDescription
description (Double -> Double -> Double -> PFunction (Double, Double)
scaleContrarilyPFunction Double
k Double
th) PSpeed
PFast (Int -> PDimension
PDimension Int
2)
  where
    description :: PDescription
description = String -> PDescription
PDescription forall a b. (a -> b) -> a -> b
$ String
"Scale contrariliy; shape: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Double
k forall a. [a] -> [a] -> [a]
++ String
", scale: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Double
th