I've been asked to approximate the integral of a one dimensional unnormalised posterior with a flat prior, using a Metropolis Hastings Markov Chain Monte Carlo, I realise that this isn't a practical endeavour, i think its more of an instructional exercise.
At the moment, I am creating a Markov chain of sampling points, $\{x_i\}$, for the distribution using the MH algorithm, and then approximating the integral of the posterior as:
\begin{equation} I \approx (\text{Prior interval}) * \frac{1}{N}\sum_i f(x_i) \end{equation}
where $f(x_i)$ is the likelihood evaluated at the point $x_i$ in the chain, and N is the length of the Markov Chain.
I have tested this algorithm in Python for numerous simple posteriors, and it converges on completely the wrong answer in every case. I've been over the code many times, and I'm almost certain there's nothing wrong with it. I also read that you need to discard the first half of the chain, and discard everything but every, say, 10th point. I've tried this and it makes no difference.
The code is shown below. It tries to integrate $\sin x$ between $0$ and $\pi$. For 20000 iterations it gives $2.45448671306 \pm 0.00502441977146$, which is obviously very wrong (answer should be 2) .The graph below shows the convergence of the algorithm
def f(x):
if 0. <= x <= np.pi:
return np.sin(x)
else:
return 0
n = 20000 #number of iterations
sigma = 0.5
x = uniform(0, np.pi) #initial x value
chain = [] #stores Markov chain
chain.append(f(x)) #initialises chain
accept = 0 #number of accepted jumps
#generates an array of random x values from norm distribution
rands = normal(0, sigma, n)
#Metropolis - Hastings algorithm
for i in range(1,n):
can = x + rands[i] #candidate for jump
aprob = min([1., f(can)/f(x)]) #acceptance probability
u = uniform(0,1) #rand number between 0 and 1
if u < aprob:
x = can
accept += 1
chain.append(f(x))
I = (np.pi)*sum(chain)*(1/len(chain)) #integral approximation
#uncertainty on approx.
uncertainty = (np.pi) / np.sqrt(len(chain)) * np.std(chain)
#integral approx at each iteration
conv = np.array([(np.pi)*sum(chain[:x+1])*(1/(x+1)) for x in range(len(chain))])
print('MCMC approx: ', I, '\t','uncertainty: ', uncertainty)
can
is rejected the current valuex
needs to be replicated, as illustrated in my code. $\endgroup$