Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
afeiguin
GitHub Repository: afeiguin/comp-phys
Path: blob/master/09_02_random_distributions.ipynb
374 views
Kernel: Python 3

Non-uniform random distributions

In the previous section we learned how to generate random deviates with a uniform probability distribution in an interval [a,b][a,b]. This distributioon is normalized, so that abP(x)dx=1.\int _a^b {P(x)dx}=1. Hence, P(x)=1/(ba)P(x)=1/(b-a).

Now, suppose that we generate a sequence {xi}\{x_i\} and we take some function of it to generate {y(xi)}={yi}\{y(x_i)\}=\{y_i\}. This new sequence is going to be distributed according to some probability density P(y)P(y), such that P(y)dy=P(x)dxP(y)dy=P(x)dx or P(y)=P(x)dxdy.P(y)=P(x)\frac{dx}{dy}.

If we want to generate a desired normalized distribution P(y)P(y), we need to solve the differential equation: dxdy=P(y).\frac{dx}{dy}=P(y). But the solution of this is x=0yP(y)dy=F(y).x=\int _0^y {P(y')dy'}=F(y). Therefore, y(x)=F1(x),y(x)=F^{-1}(x), where F1F^{-1} is the inverse of FF.

Exponential distribution

As an example, let us take y(x)=ln(x)y(x)=-\ln{(x)} with P(x)P(x) representing a uniform distribution in the interval [0,1][0,1]. Then P(y)=dxdy=ey,P(y)=\frac{dx}{dy}=e^{-y}, which is distributed exponentially. This distribution occurs frequently in real problems such as the radioactive decay of nuclei. You can also see that the quantity y/λy/\lambda has the distribution λeλy\lambda e^{-\lambda y}.

%matplotlib inline import numpy as np from matplotlib import pyplot N = 10000 r = np.random.random(N) xlambda = 0.1 x = -np.log(r)/xlambda binwidth=xlambda*5 pyplot.hist(x,bins=np.arange(0.,100., binwidth),density=True); pyplot.plot(np.arange(0.,100.,binwidth),xlambda*np.exp(-xlambda*np.arange(0.,100.,binwidth)),ls='-',c='red',lw=3);
Image in a Jupyter notebook

von Neumann rejection

A simple and ingenious method for generating random points with a probability distribution P(x)P(x) was deduced by von Neumann. Draw a plot with you probability distribution, and on the same graph, plot another curve f(x)f(x) which has finite area and lies everywhere above your original distribution. We will call f(x)f(x) the “comparison function”. Generate random pairs (xi,yi)(x_i,y_i) with uniform distribution inside f(x)f(x). Whenever the point lies inside the area of the original probability, we accept it, otherwise, we reject it. All the accepted points will be uniformly distributed within the original area, and therefore will have the desired distribution. The fraction of points accepted/rejected will deppend on the ratio between the two areas. The closer the comparison function f(x)f(x) resembles P(x)P(x), the more points will be accepted. Ideally, for P(x)=f(x)P(x)=f(x), all the points will be accepted, and none rejected. However, in practice, this is not always possible, but we can try to pick f(x)f(x) such that we minimize the fraction of rejected points.

It only remains how to pick a number with probability f(x)f(x). For this purpose, we utilize the method shown in the previous section, using a function whose indefinite intergral is know analitically, and is also analitically invertible. We then pick a random number xx and retrieve the corresponding y(x)y(x) according to ([random_invert]). Then, we generate a second random number and we use the rejection criterion.

An equivalent procedure consists of picking the second number between 0 and 1 and accept or reject according to wether is it respectively less than or greater than the ratio P(x)/f(x)P(x)/f(x). Clearly, if f(x)=P(x)f(x)=P(x) all the points will be accepted.

N = 100000 xmax = 60 ymax = xlambda rx = np.random.random(N)*xmax ry = np.random.random(N)*ymax values = [] Nin = 0 for i in range(N): if(ry[i] <= xlambda*np.exp(-xlambda*rx[i])): # Accept values.append(rx[i]) Nin += 1 x = np.asarray(values) print("Acceptance Ratio: ",Nin/float(N)) binwidth=xlambda*5 pyplot.hist(x,bins=np.arange(0.,100., binwidth),density=True); pyplot.plot(np.arange(0.,100.,binwidth),xlambda*np.exp(-xlambda*np.arange(0.,100.,binwidth)),ls='-',c='red',lw=3);
Acceptance Ratio: 0.16499
Image in a Jupyter notebook

Challenge 9.1:

Improve the acceptance ratio by using a linear function f(x)=1αxf(x)=1-\alpha x, with a ppropriate choice of α\alpha

Random walk methods: the Metropolis algorithm

Suppose that we want to generate random variables according to an arbitrary probability density P(x)P(x). The Metropolis algorithm produces a “random walk” of points {xi}\{x_i\} whose asymptotic probability approaches P(x)P(x) after a large number of steps. The random walk is defined by a “transition probability” w(xixj)w(x_i \rightarrow x_j) for one value xix_i to another xjx_j in order that the distribution of points x0x_0, x1x_1, x2x_2, ... converges to P(x)P(x). In can be shown that it is sufficient (but not necessary) to satisfy the “detailed balance” condition p(xi)w(xixj)=p(xj)w(xjxi).p(x_i)w(x_i \rightarrow x_j) = p(x_j)w(x_j \rightarrow x_i). This relation dos not specify w(xixj)w(x_i \rightarrow x_j) uniquely. A simple choice is w(xixj)=min[1,P(xj)P(xi)].w(x_i \rightarrow x_j)=\min{\left[ 1,\frac{P(x_j)}{P(x_i)} \right] }. This choice can be described by the following steps. Suppose that the “random walker” is a position xnx_n. To generate xn+1x_{n+1} we

  1. choose a trial position xt=xn+δnx_t=x_n+\delta _n , where the δn\delta _n is a random number in the interval [δ,δ][-\delta ,\delta].

  2. Calculate w=P(xt)/P(xn)w=P(x_t)/P(x_n).

  3. If w1w \geq 1 we accept the change and let xn+1=xtx_{n+1}=x_t.

  4. If w1w \leq 1, generate a random number rr.

  5. If rwr \leq w, accept the change and let xn+1=xtx_{n+1} = x_t.

  6. If the trial change is not accepted, the let xn+1=xnx_{n+1}=x_n.

It is necessary to sample a number of points of the random walk before the asymptotic probability P(x)P(x) is attained. How do we choose the “step size” δ\delta? If δ\delta is too large, only a small fraction of changes will be accepted and the sampling will be inefficient. If δ\delta is too small, a large number will be accepted, but it would take too long to sample P(x)P(x) over the whole interval of interest. Ideally, we want at least 1/3-1/2 of the trial steps to be accepted. We also want to choose x0x_0 such that the distribution {xi}\{x_i\} converges to P(x)P(x) as quickly as possible. An obvious choice is to begin the random walk at the point where P(x)P(x) is maximum.

Exercise 9.1: The Gaussian distribution

  1. Use the Metropolis algorithm to generate a Gaussian distribution P(x)=Aexp(x2/2σ2)P(x)=A \exp{(-x^2/2\sigma ^2)}. Is the numerical value of the normalization constant AA relevant? Determine the qualitative dependence of the acceptance ratio and the equilibrium time on the maximum step size δ\delta. One possible criterion for equilibrium is that x2σ2\langle x^2 \rangle \approx \sigma ^2. For σ=1\sigma = 1, what is a reasonable choice of δ\delta? (choose x0=0x_0 = 0.)

  2. Plot the asymptotic probability distribution generated by the Metropolis algorithm.

N = 100000 x = np.zeros(N) delta = 2. sigma = 20. sigma2 = sigma**2 def metropolis(xold): xtrial = np.random.random() xtrial = xold+(2*xtrial-1)*delta weight = np.exp(-0.5*(xtrial**2-xold**2)/sigma2) # weight = np.exp(-0.5*(xtrial-xold)/sigma2) # if(xtrial < 0): # weight = 0 xnew = xold if(weight >= 1): #Accept xnew = xtrial else: r = np.random.random() if(r <= weight): #Accept xnew = xtrial return xnew xwalker = 20. Nwarmup = 5 for i in range(Nwarmup): xwalker = metropolis(xwalker) x[0] = xwalker tot = x[0] for i in range(1,N): x0 = x[i-1] for j in range(10): x0 = metropolis(x0) x[i] = metropolis(x0) binwidth=sigma/10 pyplot.hist(x,bins=np.arange(-50,50., binwidth),density=True); norm = 1./(sigma*np.sqrt(2*np.pi)) pyplot.plot(np.arange(-50.,50.,binwidth),norm*np.exp(-0.5*np.arange(-50.,50.,binwidth)**2/sigma2),ls='-',c='red',lw=3);
Image in a Jupyter notebook

Challenge 9.2:

  • Modify the code above to study the equilibration "time" for different step size δ\delta.

  • Analize the acceptance ratio in terms of δ\delta.