Source code for cuqi.sampler._pcn

import numpy as np
import cuqi
from cuqi.sampler import Sampler
from cuqi.array import CUQIarray

[docs] class PCN(Sampler): # Refactor to Proposal-based sampler? _STATE_KEYS = Sampler._STATE_KEYS.union({'scale', 'current_likelihood_logd', 'lambd'})
[docs] def __init__(self, target=None, scale=1.0, **kwargs): super().__init__(target, **kwargs) self.initial_scale = scale
def _initialize(self): self.scale = self.initial_scale self.current_likelihood_logd = self._loglikelihood(self.current_point) # parameters used in the Robbins-Monro recursion for tuning the scale parameter # see details and reference in the tune method self.lambd = self.scale self.star_acc = 0.44 #TODO: 0.234 # target acceptance rate
[docs] def validate_target(self): if not isinstance(self.target, cuqi.distribution.Posterior): raise ValueError(f"To initialize an object of type {self.__class__}, 'target' need to be of type 'cuqi.distribution.Posterior'.") if not isinstance(self.prior, (cuqi.distribution.Gaussian, cuqi.distribution.Normal)): raise ValueError("The prior distribution of the target need to be Gaussian")
[docs] def step(self): # propose state xi = self.prior.sample(1).flatten() # sample from the prior x_star = np.sqrt(1-self.scale**2)*self.current_point + self.scale*xi # PCN proposal # evaluate target loglike_eval_star = self._loglikelihood(x_star) # ratio and acceptance probability ratio = loglike_eval_star - self.current_likelihood_logd # proposal is symmetric alpha = min(0, ratio) # accept/reject acc = 0 u_theta = np.log(np.random.rand()) if (u_theta <= alpha): self.current_point = x_star self.current_likelihood_logd = loglike_eval_star acc = 1 return acc
@property def prior(self): return self.target.prior @property def likelihood(self): return self.target.likelihood def _loglikelihood(self, x): return self.likelihood.logd(x) @property def dim(self): # TODO. Check if we need this. Implemented in base class if hasattr(self,'target') and hasattr(self.target,'dim'): self._dim = self.target.dim elif hasattr(self,'target') and isinstance(self.target,tuple) and len(self.target)==2: self._dim = self.target[0].dim return self._dim
[docs] def tune(self, skip_len, update_count): """ Tune the scale parameter of the PCN sampler. The tuning is based on algorithm 4 in Andrieu, Christophe, and Johannes Thoms. "A tutorial on adaptive MCMC." Statistics and computing 18 (2008): 343-373. Note: the tuning algorithm here is the same as the one used in MH sampler. """ # average acceptance rate in the past skip_len iterations hat_acc = np.mean(self._acc[-skip_len:]) # new scaling parameter zeta to be used in the Robbins-Monro recursion zeta = 1/np.sqrt(update_count+1) # Robbins-Monro recursion to ensure that the variation of lambd vanishes self.lambd = np.exp(np.log(self.lambd) + zeta*(hat_acc-self.star_acc)) # update scale parameter self.scale = min(self.lambd, 1)