Package: emcwrap
Collection of tools for Bayesian inference using DIME MCMC samplingThis provides a series of tools around the Differential-Independence Mixture Ensemble (DIME) MCMC sampler together with a nice set of statistical tools for Bayesian analysis. DIME MCMC is developed inAn Ensemble MCMC Sampler for Robust Bayesian Inference.(Gregor Boehl, 2022, SSRN No. 4250395).There exists a stand-alone implementations of the sampler in thedime_samplerpackage.The sampler has a series of advantages over conventional samplers:DIME MCMC is a (very fast) gradient-freeglobal multi-start optimizerand, at the same time, aMCMC samplerthat converges to the posterior distribution. This makes any posterior mode density maximization prior to MCMC sampling superfluous.The DIME sampler is pretty robust for odd shaped,multimodal distributions.DIME MCMC isparallelizable: many chains can run in parallel, and the necessary number of draws decreases almost one-to-one with the number of chains.DIME proposals are generated from anendogenous and adaptive proposal distribution, thereby providing close-to-optimal proposal distributions for black box target distributions without the need for manual fine-tuning.Figure: A trimodal example distribution in 35 dimensionsInstallationInstalling therepository versionfrom PyPi is as simple as typingpipinstallemcwrapin your terminal or Anaconda Prompt.UsageThe proposal can be used directly as a drop-in replacement foremcee:importemceefromdime_samplerimportDIMEMovemove=DIMEMove()...deflog_prob(x):...# define your density function, the number of chains `nchain` etc......sampler=emcee.EnsembleSampler(nchain,ndim,log_prob,moves=move)...# off you go samplingThe rest of the usage is hence analoge to Emcee, see e.g.this tutorial. The parameters specific to theDIMEMoveare documentedhere.The provided tools for Bayesian analysis are ready-to-use, but largely undocumented. Find the module documentation here:https://emcwrap.readthedocs.io/en/latest/modules.html.TutorialLets look at an example. Let’s define a nice and challenging distribution (it’s the distribution from the figure above):# some importimportemcwrapasewimportnumpyasnpimportscipy.statsasssfromemcwrap.test_allimport_create_test_func,_marginal_pdf_test_func# make it reproduciblenp.random.seed(0)# define distributionm=2cov_scale=0.05weight=(0.33,.1)ndim=35initvar=np.sqrt(2)log_prob=_create_test_func(ndim,weight,m,cov_scale)log_probwill now return the log-PDF of a 35-dimensional Gaussian mixture withthree separate modes.Next, define the initial ensemble. In a Bayesian setup, a good initial ensemble would be a sample from the prior distribution. Here, we will go for a sample from a rather flat Gaussian distribution.# number of chains and number of iterationsnchain=ndim*5niter=5000# initial ensembleinitmean=np.zeros(ndim)initcov=np.eye(ndim)*np.sqrt(2)initchain=ss.multivariate_normal(mean=initmean,cov=initcov).rvs(nchain)Setting the number of parallel chains to5*ndimis a sane default. For highly irregular distributions with several modes you should use more chains. Very simple distributions can go with less.Now let the sampler run for 5000 iterations.# use the DIME proposalmoves=ew.DIMEMove(aimh_prob=0.1,df_proposal_dist=10)sampler=ew.run_mcmc(log_prob,niter,p0=initchain,moves=moves)[ll/MAF: 11.598(4e+00)/23%]: 100%|████████████████████ 5000/5000 [00:18<00:00, 164.70sample(s)/s]The setting ofaimh_probis the actual default value. For less complex distributions (e.g. distributions closer to Gaussian) a higher value can be chosen, which accelerates burn-in. The information in the progress bar has the structure[ll/MAF: <maximumlog-prob>(<standarddeviation oflog-prob>)/<meanacceptancefraction>]....Note that if you wish to use emcee directly instead of the wrapper, you could simply do the following, which will give you the same result:importemceesampler=emcee.EnsembleSampler(nchain,ndim,log_prob,moves=moves)sampler.run_mcmc(initchain,int(niter),progress=True)The following code creates the figure above, which is a plot of the marginal distribution along the first dimension (remember that this actually is a 35-dimensional distribution). For plotting, this tutorial assumesgrgrlibto be installed (just runpip install grgrlibin terminal/prompt).fromgrgrlibimportfigurator# get elementschain=sampler.get_chain()lprob=sampler.get_log_prob()# plottingfigs,axs=figurator(1,1,1,figsize=(9,6))axs[0].hist(chain[-niter//2:,:,0].flatten(),bins=50,density=True,alpha=0.2,label="Sample")xlim=axs[0].get_xlim()x=np.linspace(xlim[0],xlim[1],100)axs[0].plot(x,ss.norm(scale=np.sqrt(initvar)).pdf(x),"--",label="Initialization")axs[0].plot(x,ss.t(df=10,loc=moves.prop_mean[0],scale=moves.prop_cov[0,0]**0.5).pdf(x),":",label="Final proposals")axs[0].plot(x,_marginal_pdf_test_func(x,cov_scale,m,weight),label="Target")axs[0].legend()To ensure proper mixing, let us also have a look at the MCMC traces, again focussing on the first dimension.figs,axs=figurator(1,1,1)axs[0].plot(chain[:,:,0],alpha=0.05,c="C0")Note how chains are also switching between the three modes because of the global proposal kernel.While DIME is an MCMC sampler, it can straightforwardly be used as a global optimization routine. To this end, specify some broad starting region (in a non-Bayesian setup there is no prior) and let the sampler run for an extended number of iterations. Finally, assess whether the maximum value per ensemble did not change much in the last few hundred iterations. In a normal Bayesian setup, plotting the associated log-likelihood over time also helps to assess convergence to the posterior distribution.figs,axs=figurator(1,1,1)axs[0].plot(lprob,alpha=0.05,c="C0")axs[0].plot(np.arange(niter),np.max(lprob)*np.ones(niter),"--",c="C1")ReferencesIf you are using this software in your research, please cite@techreport{boehl2022mcmc,author={Gregor Boehl},title={Ensemble MCMC Sampling for Robust Bayesian Inference},journal={Available at SSRN 4250395},year={2022}}