Example #1
0
    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()
Example #2
0
    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
Example #3
0
    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()