蒙特卡洛模拟XY伊辛模型
前言故事
世界上最早的通用电子计算机之一----ENIAC在发明后即被用于曼哈顿计划,乌拉姆敏锐地意识到在计算机的帮助下,可通过重复数百次模拟过程的方式来对概率变量进行统计估计。冯诺依曼立即认识到这个想法的重要性并给予支持。1947年,乌拉姆提出这种统计方法并应用于计算裂变的连锁反应。由于乌拉姆常说他的叔叔又在蒙特卡洛赌场输钱了,因此他的同事Nicolas Metropolis 戏称该方法为“蒙特卡洛”,不料却流传开去。
算法说明
参考文档:维基百科
1,把握这个算法,首先要体会到随机的意思。这是一个通过随机初始化,然后进行优化的一种算法。
2,直接上算法流程,之后再进行解释。
一,初始化:选择任意一个x0,假设我们的选择服从某种概率分布,比如高斯分布或者平均分布。 |
二,for i from 0 to N 开始循环迭代 |
(1)Generate 产生一个候选x 。最简单的方式,随机一个 |
(2)Calculate 计算接受比 alpha 这里定义的接受比等于 新产生的x在某种分布中的概率/x0在某种分布中的概率。这里的某种分布跟前面的高斯或者平均不是一回事,这里的分布更多的要体现问题的方面。这也是问题在这个算法中唯一的一次介入。 |
(3)Accepted? |
(3_1) 产生一个随机数u,其范围在[0,1] |
(3_2) if u<=alpha 那么候选的x就去掉候选状况,正式替代 |
(3_3)if u>alpha 那么候选的x无效 |
说明:1,为什么u<alpha才有效替换?
我们可以看到,当p(before)=p(after)时,这时,按照我们的常人观点,此时变不变是一半对一半!因为两者的概率一样,所以发生的概率应该一样的。但是这里并不是,如果发生了这种事,直接替换。
当p(after)<p(before)时,此时变化后的状态存在的概率要低于不变的概率,此时并不是一定不变,仍然以一定的概率变。
说明2,第(2)步的计算概率是最跟问题有关的。前面的都是随机分布变化,跟问题一点关系没有。请问,问题的概率分布一定存在吗?如果存在,一般怎么构造这种概率表达式?
我也没接触太多。目前,只见到过这个伊辛模型。在热力学统计中,确实有一个物理量对应这种概率分布是多少,称为配分函数。这里是配分函数的统计意义解释。
代码实现
import random import matplotlib.pyplot as plt import numpy as np import copy import math import time '''这个就是MCMC模拟,用来模拟某个温度下XY Ising模型分布, 几点注意: 注意1,二维伊辛模型,我们用矩阵来模拟。 注意2,旋转的方向,我们用0到2pi表示吧 算法过程: 一,用一个对称的分布,高斯分布初始化矩阵 二,下面是循环 (1)产生一个候选的自旋方向,为了连续,假设新产生的自旋方向变化最多比原来变化pi/2 也就是旋转90度 (2)计算两个概率,这里热统中的配分函数正比于概率,因此我们用配分函数Z的比值 Z(变化后)/Z(变化前)=exp(-(E后-E前)/kT) ,这里k是玻尔兹曼常数,T是开尔文温度。令 这个值是alpha (3)判断是都接受*********这个规则有进一步讨论************* (3_1)产生一个随机数u在【0,1】之间 (3_2)如果u<=alhpa,接受候选的,改变此时的自旋状态 (3_3)如果u>alpha,不接受候选的,不改变此时的自旋状态 inputdata: S :matrix 随机分布,假设已经产生 param: T 温度 delta 最大的变化度数,默认是90度,也可以调整为其他 outputdata:S ''' def MetropolisHastings(S,T,numsOfItera): deltamax=0.5 k = 1 #玻尔兹曼常数 for sdw in range(numsOfItera): # k2 = np.random.randint(0,S.shape[0]**2) i = np.random.randint(0,S.shape[0]) j = np.random.randint(0,S.shape[0]) # print('产生的随机位置是:',i,j) #time.sleep(0.1) for m in range(1): delta = (2*np.random.random()-1)*deltamax newAngle = S[i][j]+delta # print(delta) energyBefore = getEnergy(i=i,j=j,S=S,angle=None) energyLater = getEnergy(i,j,S=S,angle=newAngle) alpha = math.exp(-(energyLater-energyBefore)/(k*T)) #print(alpha) # if alpha>=1: # print('大于1的哦') if alpha >=1: S[i][j]=newAngle elif np.random.uniform(0.0,1.0)<=1.0*alpha: S[i][j]=newAngle return S def getEnergy(i,j,S,angle=None): width = S.shape[0] height = S.shape[1] # print('矩阵的宽和高是',width,height) top_i = i-1 if i>0 else width-1 bottom_i = i+1 if i<(width-1) else 0 left_j = j-1 if j>0 else height-1 right_j = j+1 if j<(height-1) else 0 enviroment = [[top_i,j],[bottom_i,j],[i,left_j],[i,right_j]] # print(i,j,enviroment) #print(enviroment) energy = 0 if angle ==None: # print('angle==None') for num_i in range(0,4,1): energy += -np.cos(S[i][j]-S[enviroment[num_i][0]][enviroment[num_i][1]]) else: # print('Angle') for num_i in range(0,4,1): energy += -np.cos(angle-S[enviroment[num_i][0]][enviroment[num_i][1]]) return energy #S=2*np.pi*np.random.rand(30,30) def calculateAllEnergy(S): energy =0 for i in range(len(S)): for j in range(len(S[0])): energy +=getEnergy(i,j,S) averageEnergy = energy/(len(S[0])*len(S)) return averageEnergy/2 #print(S) #for j in range(1000): # print(j) # MetropolisHastings(S,10) def getWeightValue(numsOfSample,sizeOfSample,temperature): for i in range(numsOfSample): #产生个数 print('+++++++正在计算第%s个样本++++++++++'%i) S=2*np.pi*np.random.random((sizeOfSample,sizeOfSample)) initialEnergy = calculateAllEnergy(S) print('系统的初始能量是:',initialEnergy) newS = np.array(copy.deepcopy(S)) for nseeps in range(100): newS = MetropolisHastings(newS,temperature,sizeOfSample**2) aveEnergy = calculateAllEnergy(newS) plot(newS) print('系统的平均能量是:',aveEnergy) reshaped = np.reshape(newS,(1,sizeOfSample**2)) if i ==0: s = copy.deepcopy(reshaped) continue else: s = np.row_stack((s,reshaped)) return s res = getWeightValue(1,40,2) #print(len(res)) def plot(S): X, Y = np.meshgrid(np.arange(0,S.shape[0]),np.arange(0,S.shape[0])) U = np.cos(S) V = np.sin(S) plt.figure() #plt.title('Arrows scale with plot width, not view') Q = plt.quiver(X, Y, U, V, units='inches') #qk = plt.quiverkey(Q, 0.3, 0.3, 1, r'$2 \frac{m}{s}$', labelpos='E', # coordinates='figure') plt.show()
结果展示:
系统值是:
+++++++正在计算第0个样本++++++++++ 系统的初始能量是: 0.006919117595 系统的平均能量是: -0.494289314266 +++++++正在计算第0个样本++++++++++ 系统的初始能量是: 0.154688600481 系统的平均能量是: -0.315447987184 +++++++正在计算第0个样本++++++++++ 系统的初始能量是: 0.00142378493144 系统的平均能量是: -0.588001265121
跟点击打开链接对比,
我们看到在温度等于2的时候,还是很类似的。