def sample(self, ndraw = 1000, adaptationConstant = .90,alpha = .5, steps = 10, debug = Inf, ndraw_max = 20000 , nChains = 5, burnIn = 100, thin = 1, convergenceCriteria = 1.1, mConvergence = False, mAccept = False): """ Samples from a posterior distribution using DREAM. Parameters ---------- ndraw : int minimum number of draws from the sample distribution to be returned ndraw_max : int maximum number of draws from the sample distribution to be returned nChains : int number of different chains to employ burnInSize : int number of iterations (meaning draws / nChains) to do before doing actual sampling. Returns ------- None : None sample sets self.history which contains the combined draws for all the chains self.iter which is the total number of iterations self.acceptRatio which is the acceptance ratio self.burnIn which is the number of burn in iterations done self.R which is the gelman rubin convergence diagnostic for each dimension """ startTime = time.time() maxChainDraws = floor(ndraw_max/nChains) self._initChains(nChains, ndraw_max) # get the starting log likelihood and position for each of the chains currentVectors =random.normal(loc = 1.0, scale = .01, size = (self._nChains, self.dimensions)) #self._vectors' currentVectors[:,0] -= 1.0 self._propose(currentVectors) currentLogPs = self._logPs currentGradLogPs = self._gradLogPs #initialize the history arrays combinedHistory = zeros((nChains * maxChainDraws , self.dimensions)) sequenceHistories = zeros((nChains, self.dimensions, maxChainDraws)) logPSequences = zeros((nChains, maxChainDraws)) #add the starting positions to the history sequenceHistories[:,:,0] = currentVectors combinedHistory[0:nChains,:] = currentVectors logPSequences[:, 0] = currentLogPs # initilize the convergence diagnostic object grConvergence = GRConvergence() #2)now loop through and sample minDrawIters = ceil(ndraw / nChains) maxIters = ceil(ndraw_max /nChains) iter = 1 acceptsRatio = 0 relevantHistoryStart = 0 relevantHistoryEnd = 1 lastRecalculation = 0 adaptedMean = 0 adaptedCov = 1.0 # continue sampling if: # 1) we have not drawn enough samples to satisfy the minimum number of iterations # 2) or any of the dimensions have not converged # 3) and we have not done more than the maximum number of iterations momentumVectors = 0 while ( relevantHistoryStart < burnIn or (relevantHistoryEnd - relevantHistoryStart) *nChains < ndraw or any(grConvergence.R > convergenceCriteria)) and relevantHistoryEnd*nChains < ndraw_max: adaptedMean, adaptedCov = self._adapt(currentVectors,adaptationConstant/max(relevantHistoryStart-burnIn, 1), adaptedMean, adaptedCov) # generate momentum vectors momentumVectors = momentumVectors * alpha + .5 * (1- alpha**2) * random.normal(size = (self._nChains, self.dimensions)) stepMomentum = momentumVectors stepGradLogPs = currentGradLogPs scale = diagonal(adaptedCov) if iter % debug == 0 : print "c", currentVectors print "cM", momentumVectors print "cp", currentLogPs print "cg", currentGradLogPs for i in range(steps): halfStepMomentum = stepMomentum + (.5/steps) * stepGradLogPs stepVectors = currentVectors + (.5/steps) * halfStepMomentum * scale self._propose(stepVectors) stepGradLogPs = self._gradLogPs stepMomentum = halfStepMomentum + (.5/steps) * stepGradLogPs if ( iter %debug == 0): print i print "s", stepVectors print "sM", stepMomentum print "sg", stepGradLogPs proposalVectors = stepVectors proposalLogPs = self._logPs proposalGradLogPs = stepGradLogPs #apply the metrop decision to decide whether to accept or reject each chain proposal decisions, acceptance = self._metropolis_hastings(currentLogPs + sum(momentumVectors**2 / (2 )* scale, axis = 1), proposalLogPs+ sum(stepMomentum**2 / (2 )* scale, axis = 1)) weighting = 1 - exp(-1.0/60) acceptsRatio = weighting * sum(acceptance)/nChains + (1-weighting) * acceptsRatio if mAccept and iter % 20 == 0: print acceptsRatio #make the current vectors the previous vectors previousVectors = currentVectors currentVectors = choose(decisions[:,newaxis], (currentVectors, proposalVectors)) currentLogPs = choose(decisions, (currentLogPs, proposalLogPs)) currentGradLogPs = choose(decisions[:, newaxis], (currentGradLogPs, proposalGradLogPs)) #need to repropose these because some of these may have been rolled back more than 1 "proposal" self._propose(currentVectors) # we only want to recalculate convergence criteria when we are past the burn in period # and then only every so often (currently every 10% increase in iterations) if (relevantHistoryStart > burnIn and (relevantHistoryEnd - relevantHistoryStart) * nChains > ndraw and iter > lastRecalculation * 1.1): lastRecalculation = iter # calculate the Gelman Rubin convergence diagnostic grConvergence.update(sequenceHistories, relevantHistoryEnd, relevantHistoryStart, self.dimensions, nChains) if mConvergence: print mean(grConvergence.R), std(grConvergence.R), max(grConvergence.R), argmax(grConvergence.R) #record the vector for each chain if iter % thin == 0: sequenceHistories[:,:,relevantHistoryEnd] = currentVectors combinedHistory[(relevantHistoryEnd *nChains) :(relevantHistoryEnd *nChains + nChains),:] = currentVectors logPSequences[:, relevantHistoryEnd] = currentLogPs relevantHistoryEnd += 1 relevantHistoryStart += .5 iter += 1 #3) finalize # only make the second half of draws available because that's the only part used by the convergence diagnostic self.history = combinedHistory[relevantHistoryStart*nChains:relevantHistoryEnd*nChains,:] self.iter = iter self.acceptRatio = acceptsRatio self.burnIn = burnIn self.time = time.time() - startTime self.R = grConvergence.R self._finalizeChains()
def sample(self, ndraw = 1000, samplesPerAdapatationParameter = 5, adaptationDecayLength = 250, variables_of_interest = None,minimum_scale = .1, maxGradient = 1.0, ndraw_max = None , nChains = 5, burnIn = 1000, thin = 2, convergenceCriteria = 1.1, monitor_convergence = True, monitor_acceptence = True): """Samples from a posterior distribution using Adaptive Metropolis Adjusted Langevin Algorithm (AMALA). Parameters ---------- ndraw : int minimum number of draws from the sample distribution to be returned ndraw_max : int maximum number of draws from the sample distribution to be returned nChains : int number of different chains to employ burnInSize : int number of iterations (meaning draws / nChains) to do before doing actual sampling. minimum_scale : float the minimum that the scaling constant can fall to (default .1) monitor_convergence : bool determines whether to periodically print out convergence statistics (True) monitor_acceptence : bool determines whether to periodically print out the average acceptance ratio and adapted scale Returns ------- None : None sample sets self.history which contains the combined draws for all the chains self.iter which is the total number of iterations self.burnIn which is the number of burn in iterations done self.R which is the gelman rubin convergence diagnostic for each dimension """ startTime = time.time() if ndraw_max is None: ndraw_max = 10 * ndraw maxChainDraws = floor(ndraw_max/nChains) self._initChains(nChains, ndraw_max) history = SimulationHistory(self.slices, maxChainDraws,self._nChains, self.dimensions) if variables_of_interest is not None: slices = [] for var in variables_of_interest: slices.append(self.slices[var]) else: slices = [slice(None,None)] history.add_group('interest', slices) # initilize the convergence diagnostic object convergence_diagnostics = [GRConvergence(.1, history), CovarianceConvergence(.3, history), CovarianceConvergence(.2, history, 'interest')] monitor_diagnostics = [convergence_diagnostics[0], convergence_diagnostics[2]] iter = 1 lastRecalculation = 0 # try to find some approximate modes for starting the chain for chain in self._chains: inv_hessian = find_mode(self,chain) adaptationConstant = min(self._nChains*1.0/(self.dimensions * samplesPerAdapatationParameter), 1) adapted_approximation = AdaptedApproximation(self._chains[1].vector, inv_hessian) adapted_scale = AdaptedScale(self.optimalAcceptance, minimum_scale) accepts_ratio_weighting = 1 - exp(-1.0/30) adaptationDecay = 1.0/adaptationDecayLength # continue sampling if: # 1) we have not drawn enough samples to satisfy the minimum number of iterations # 2) or any of the dimensions have not converged # 3) and we have not done more than the maximum number of iterations while ( (history.nsamples < ndraw or not all((diagnostic.converged() for diagnostic in convergence_diagnostics))) and history.ncomplete_sequence_histories < maxChainDraws - 1): if iter == burnIn: history.start_sampling() current_logps = self.logps jump_logp, reverse_logp = propose_amala(self,adapted_approximation, adapted_scale, maxGradient) acceptance = self.metropolis_hastings(current_logps,self.logps, jump_logp, reverse_logp) self._update_accepts_ratio(accepts_ratio_weighting, acceptance) if monitor_acceptence and iter % 20 == 0: print "accepts ratio: ", self.accepts_ratio, " adapted scale: ", adapted_scale.scale if history.nsamples > ndraw and history.nsamples > lastRecalculation * 1.1: lastRecalculation = history.nsamples for diagnostic in convergence_diagnostics: diagnostic.update() for diagnostic in monitor_diagnostics: print diagnostic.state() if iter % thin == 0: history.record(self.vectors, self.logps, .5) adaptation_rate = exp(-adaptationConstant/exp(history.nsamples * adaptationDecay)) adapted_approximation.update(self.vectors, adaptation_rate) mean(self.vectors) adapted_scale.update(acceptance, adaptation_rate) iter += 1 self.finalize_chains() return history , time.time() - startTime
def sample(self, ndraw = 1000, ndraw_max = 20000 , nChains = 5, burnIn = 100, thin = 5, convergenceCriteria = 1.1,variables_of_interest = None, nCR = 3, DEpairs = 1, adaptationRate = .65, eps = 5e-6, mConvergence = False, mAccept = False): """ Samples from a posterior distribution using DREAM. Parameters ---------- ndraw : int minimum number of draws from the sample distribution to be returned ndraw_max : int maximum number of draws from the sample distribution to be returned nChains : int number of different chains to employ burnInSize : int number of iterations (meaning draws / nChains) to do before doing actual sampling. nCR : int number of intervals to use to adjust the crossover probability distribution for efficiency DEpairs : int number of pairs of chains to base movements off of eps : float used in jittering the chains Returns ------- None : None sample sets self.history which contains the combined draws for all the chains self.iter which is the total number of iterations self.acceptRatio which is the acceptance ratio self.burnIn which is the number of burn in iterations done self.R which is the gelman rubin convergence diagnostic for each dimension """ startTime = time.time() maxChainDraws = floor(ndraw_max/nChains) self._initChains(nChains, ndraw_max) history = SimulationHistory(maxChainDraws, self._nChains, self.dimensions) if variables_of_interest is not None: slices = [] for var in variables_of_interest: slices.append(self.slices[var]) else: slices = [slice(None,None)] history.add_group('interest', slices) # initialize the temporary storage vectors currentVectors = zeros((nChains, self.dimensions)) currentLogPs = zeros(nChains) #) make a list of starting chains that at least spans the dimension space # in this case it will be of size 2*dim nSeedChains = int(ceil(self.dimensions* 2/nChains) * nChains) nSeedIterations = int(nSeedChains/nChains) model = self._model_generator() for i in range(nSeedIterations - 1): vectors = zeros((nChains, self.dimensions)) for j in range(nChains): #generate a vector drawn from the prior distributions for variable in model: if isinstance(variable,Stochastic) and not variable.observed: drawFromPrior = variable.random() if isinstance(drawFromPrior , np.matrix): drawFromPrior = drawFromPrior.A.ravel() elif isinstance(drawFromPrior, np.ndarray): drawFromPrior = drawFromPrior.ravel() else: drawFromPrior = drawFromPrior vectors[j,self.slices[str(variable)]] = drawFromPrior history.record(vectors,0,0) #use the last nChains chains as the actual chains to track vectors = self._vectors #add the starting positions to the history history.record(vectors,self._logPs,0) gamma = None # initilize the convergence diagnostic object grConvergence = GRConvergence() covConvergence = CovarianceConvergence() # get the starting log likelihood and position for each of the chains currentVectors = vectors currentLogPs = self._logPs #2)now loop through and sample minDrawIters = ceil(ndraw / nChains) maxIters = ceil(ndraw_max /nChains) iter = 0 accepts_ratio_weighting = 1 - exp(-1.0/30) lastRecalculation = 0 # continue sampling if: # 1) we have not drawn enough samples to satisfy the minimum number of iterations # 2) or any of the dimensions have not converged # 3) and we have not done more than the maximum number of iterations while ( history.nsamples < ndraw or any(grConvergence.R > convergenceCriteria)) and history.ncombined_history < ndraw_max: if iter == burnIn: history.start_sampling() #every5th iteration allow a big jump if random.randint(5) == 0.0: gamma = array([1.0]) else: gamma = array([2.38 / sqrt( 2 * DEpairs * self.dimensions)]) proposalVectors = dream_components.dream_proposals(currentVectors, history,self.dimensions, nChains, DEpairs, gamma, .05, eps) # get the log likelihoods for the proposal chains self._propose(proposalVectors) proposalLogPs = self._logPs #apply the metrop decision to decide whether to accept or reject each chain proposal decisions, acceptance = self._metropolis_hastings(currentLogPs,proposalLogPs) self._update_accepts_ratio(accepts_ratio_weighting, acceptance) if mAccept and iter % 20 == 0: print self.accepts_ratio self._reject(decisions) #make the current vectors the previous vectors previousVectors = currentVectors currentVectors = choose(decisions[:,newaxis], (currentVectors, proposalVectors)) currentLogPs = choose(decisions, (currentLogPs, proposalLogPs)) # we only want to recalculate convergence criteria when we are past the burn in period if history.nsamples > 0 and iter > lastRecalculation * 1.1 and history.nsequence_histories > self.dimensions: lastRecalculation = iter grConvergence.update(history) covConvergence.update(history,'all') covConvergence.update(history,'interest') if mConvergence: print mean(grConvergence.R), std(grConvergence.R), max(grConvergence.R), argmax(grConvergence.R) print covConvergence.relativeVariances['interest'] if iter % thin == 0: historyStartMovementRate = adaptationRate #try to adapt more when the acceptance rate is low and less when it is high if adaptationRate == 'auto': historyStartMovementRate = min((.234/self.accepts_ratio)*.5, .95) history.record(currentVectors, currentLogPs, historyStartMovementRate) iter += 1 #3) finalize # only make the second half of draws available because that's the only part used by the convergence diagnostic self.history = history.samples self.iter = iter self.burnIn = burnIn self.time = time.time() - startTime self.R = grConvergence.R self._finalizeChains()