import numpy as np, matplotlib.pyplot as plt
rng = np.random.default_rng(0); cen = np.array([[-2,0],[2,1]]); s2 = 0.7**2
def logp(x): d=x-cen; q=-0.5*np.sum(d*d,1)/s2; m=q.max(); return m+np.log(np.exp(q-m).sum())
def score(x): d=x-cen; w=np.exp(-0.5*np.sum(d*d,1)/s2); w/=w.sum(); return -(w[:,None]*d).sum(0)/s2
x=np.zeros(2); M=[] # Metropolis
for _ in range(4000):
xp=x+rng.normal(0,0.8,2)
if np.log(rng.random())<logp(xp)-logp(x): x=xp
M.append(x.copy())
x=np.zeros(2); L=[]; eps=0.05 # Langevin
for _ in range(4000):
x=x+eps*score(x)+np.sqrt(2*eps)*rng.normal(0,1,2); L.append(x.copy())
M,L=np.array(M[200:]),np.array(L[200:])
gx,gy=np.meshgrid(np.linspace(-5,5,120),np.linspace(-4,4,120))
Z=np.array([np.exp(logp(np.array([a,b]))) for a,b in zip(gx.ravel(),gy.ravel())]).reshape(gx.shape)
fig,ax=plt.subplots(1,2,figsize=(6.8,2.7))
for a,S,t in [(ax[0],M,"Metropolis–Hastings"),(ax[1],L,"Langevin")]:
a.contour(gx,gy,Z,levels=6,cmap="Greys"); a.plot(S[:,0],S[:,1],".",ms=1,alpha=.3,color="#5d2c80")
a.set_title(t,fontsize=9); a.set_xticks([]); a.set_yticks([])
plt.tight_layout(); plt.show()