# -*- coding: utf-8 -*-
# The program below is extracted from the following reference:
# Lepage, G. Peter. 1998. Lattice QCD for novices. In *Strong Interactions
# at Low and Intermediate Energies: Proceedings of the 13th Annual HUGS
# at CEBAF, Jefferson Laboratory* (edited by J. L. Goity), pp. 49–90.
# River Edge, N.J.: World Scientific.
# Preprint available at arxiv.org/abs/hep-lat/0506036
# To understand what the code does and how to interpret the results, please
# see the Lepage article.
# The only changes made in the published program are minor modifications
# needed to make it compatible with more-recent versions of Python. In
# particular, the older Numeric package has been replaced with the modern
# NumPy package.
import numpy
from random import uniform
from math import *
def update(x):
for j in range(0,N):
old_x = x[j] # save original value
old_Sj = S(j,x)
x[j] = x[j] + uniform(-eps,eps) # update x[j]
dS = S(j,x) - old_Sj # change in action
if dS > 0 and exp(-dS) < uniform(0,1):
x[j] = old_x # restore old value
def S(j,x): # harm. osc. S
jp = (j+1) % N # next site
jm = (j-1) % N # previous site
return a*x[j]**2/2 + x[j]*(x[j]-x[jp]-x[jm])/a
def compute_G(x,n):
g = 0
for j in range(0,N):
g = g + x[j]*x[(j+n) % N]
return g/N
def MCaverage(x,G):
for j in range(0,N): # initialize x
x[j] = 0
for j in range(0,5*N_cor): # thermalize x
update(x)
for alpha in range(0,N_cf): # loop on random paths
for j in range(0,N_cor):
update(x)
for n in range(0,N):
G[alpha][n] = compute_G(x,n)
for n in range(0,N): # compute MC averages
avg_G = 0
for alpha in range(0,N_cf):
avg_G = avg_G + G[alpha][n]
avg_G = avg_G/N_cf
print "G(%d) = %g" % (n,avg_G)
def bootstrap(G):
N_cf = len(G)
G_bootstrap = [] # new ensemble
for i in range(0,N_cf):
alpha = int(uniform(0,N_cf)) # choose random config
G_bootstrap.append(G[alpha]) # keep G[alpha]
return G_bootstrap
def bin(G,binsize):
G_binned = [] # binned ensemble
for i in range(0,len(G),binsize): # loop on bins
G_avg = 0
for j in range(0,binsize): # loop on bin elements
G_avg = G_avg + G[i+j]
G_binned.append(G_avg/binsize) # keep bin avg
return G_binned
# set parameters:
N = 20
N_cor = 20
N_cf = 1000
a = 0.5
eps = 1.4
# create arrays:
x = numpy.zeros((N,), dtype=float)
G = numpy.zeros((N_cf,N), dtype=float)
# do the simulation:
MCaverage(x,G)
# If the file is called qcd.py, it is run with the command:
# 'python qcd.py'
#
# To test the binning and bootstrap codes add the following
# to the the file:
def avg(G): # MC avg of G
return numpy.sum(G,axis=0)/len(G)
def sdev(G): # std dev of G
g = numpy.asarray(G)
return numpy.absolute(avg(g**2)-avg(g)**2)**0.5
print 'avg G\n',avg(G)
print 'avg G (binned)\n',avg(bin(G,4))
print 'avg G (bootstrap)\n',avg(bootstrap(G))
# The average of the binned copy of G should be the same as the
# average of G itself; the average of the bootstrap copy should
# be different by an amount of order the Monte Carlo error.
# Compute averages for several bootstrap copies to get a good
# feel for the errors. Finally one wants to extract energies.
# This is done by adding code to compute ∆E(t):
def deltaE(G): # Delta E(t)
avgG = avg(G)
adE = numpy.log(numpy.absolute(avgG[:-1]/avgG[1:]))
return adE/a
print 'Delta E\n',deltaE(G)
# print 'Delta E (bootstrap)\n',deltaE(bootstrap(G))
# Again repeating the evaluation for 50 or 100 bootstrap
# copies of G gives an estimate of the statistical errors
# in the energies. Additional code can be added to evaluate
# standard deviations from these copies:
def bootstrap_deltaE(G,nbstrap=100): # Delta E + errors
avgE = deltaE(G) # avg deltaE
bsE = []
for i in range(nbstrap): # bs copies of deltaE
g = bootstrap(G)
bsE.append(deltaE(g))
bsE = numpy.array(bsE)
sdevE = sdev(bsE) # spread of deltaE's
print "\n%2s %10s %10s" % ("t","Delta E(t)","error")
print 2 *"-"
for i in range(len(avgE)/2):
print "%2d %10g %10g" % (i,avgE[i],sdevE[i])
bootstrap_deltaE(G)