我正在尝试在Barabasi-Albert网络中模拟Ising相变,并尝试复制在Ising网格模拟中会观察到的一些可观察到的结果,例如磁化和能量。但是,我在解释结果时遇到了麻烦:不确定物理学是否错误或实现中是否有错误。这是一个最小的工作示例:

import numpy as np
import networkx as nx
import random
import math

## sim params

# coupling constant
J = 1.0 # ferromagnetic

# temperature range, in units of J/kT
t0 = 1.0
tn = 10.0
nt = 10.
T = np.linspace(t0, tn, nt)

# mc steps
steps = 1000

# generate BA network, 200 nodes with preferential attachment to 3rd node
G = nx.barabasi_albert_graph(200, 3)

# convert csr matrix to adjacency matrix, a_{ij}
adj_matrix = nx.adjacency_matrix(G)
top = adj_matrix.todense()
N = len(top)

# initialize spins in the network, ferromagnetic
def init(N):
    return np.ones(N)

# calculate net magnetization
def netmag(state):
    return np.sum(state)

# calculate net energy, E = \sum J *a_{ij} *s_i *s_j
def netenergy(N, state):
    en = 0.
    for i in range(N):
        for j in range(N):
            en += (-J)* top[i,j]*state[i]*state[j]
    return en

# random sampling, metropolis local update
def montecarlo(state, N, beta, top):

    # initialize difference in energy between E_{old} and E_{new}
    delE = []

    # pick a random source node
    rsnode = np.random.randint(0,N)

    # get the spin of this node
    s2 = state[rsnode]

    # calculate energy by summing up its interaction and append to delE
    for tnode in range(N):
        s1 = state[tnode]
        delE.append(J * top[tnode, rsnode] *state[tnode]* state[rsnode])

    # calculate probability of a flip
    prob = math.exp(-np.sum(delE)*beta)

    # if this probability is greater than rand[0,1] drawn from an uniform distribution, accept it
    # else retain current state
    if prob> random.random():
        s2 *= -1
    state[rsnode] = s2

    return state

def simulate(N, top):

    # initialize arrays for observables
    magnetization = []
    energy = []
    specificheat = []
    susceptibility = []

    for count, t in enumerate(T):


        # some temporary variables
        e0 = m0 = e1 = m1 = 0.

        print 't=', t

        # initialize spin vector
        state = init(N)

        for i in range(steps):

            montecarlo(state, N, 1/t, top)

            mag = netmag(state)
            ene = netenergy(N, state)

            e0 = e0 + ene
            m0 = m0 + mag
            e1 = e0 + ene * ene
            m1 = m0 + mag * mag

        # calculate thermodynamic variables and append to initialized arrays
        energy.append(e0/( steps * N))
        magnetization.append( m0 / ( steps * N))
        specificheat.append( e1/steps - e0*e0/(steps*steps) /(N* t * t))
        susceptibility.append( m1/steps - m0*m0/(steps*steps) /(N* t *t))

        print energy, magnetization, specificheat, susceptibility

        plt.figure(1)
        plt.plot(T, np.abs(magnetization), '-ko' )
        plt.xlabel('Temperature (kT)')
        plt.ylabel('Average Magnetization per spin')

        plt.figure(2)
        plt.plot(T, energy, '-ko' )
        plt.xlabel('Temperature (kT)')
        plt.ylabel('Average energy')

        plt.figure(3)
        plt.plot(T, specificheat, '-ko' )
        plt.xlabel('Temperature (kT)')
        plt.ylabel('Specific Heat')

        plt.figure(4)
        plt.plot(T, susceptibility, '-ko' )
        plt.xlabel('Temperature (kT)')
        plt.ylabel('Susceptibility')

simulate(N, top)

观察结果:
  • Magnetization trend as a function of temperature
  • Specific Heat

  • 我尝试对代码进行大量注释,以防万一我忽略了某些内容,请询问。

    问题:
  • 磁化趋势正确吗?磁化强度随温度升高而降低,但无法确定相变的临界温度。
  • 能量随温度升高而接近零,这似乎与在伊辛电网中观察到的相干。为什么我得到负的比热值?
  • 如何选择蒙特卡洛步数?这仅仅是根据网络节点的数量进行的尝试吗?

  • 编辑:02.06:python - Ising模型[Python]-LMLPHP:反铁磁配置的模拟崩溃

    最佳答案

    首先,由于这是一个编程站点,让我们分析一下程序。您的计算效率很低,这使得探索更大的图变得不切实际。在您的情况下,邻接矩阵是200x200(40000)个元素,只有大约3%的非零。将其转换为密集矩阵意味着需要更多的计算,同时还要评估montecarlo例程中的能量差和netenergy中的净能量。以下代码在我的系统上执行速度提高了5倍,并且在较大的图形上可以实现更好的加速效果:

    # keep the topology as a sparse matrix
    top = nx.adjacency_matrix(G)
    
    def netenergy(N, state):
        en = 0.
        for i in range(N):
            ss = np.sum(state[top[i].nonzero()[1]])
            en += state[i] * ss
        return -0.5 * J * en
    

    注意因子中的0.5-由于邻接矩阵是对称的,因此每对自旋都被计数两次!

    def montecarlo(state, N, beta, top):
        # pick a random source node
        rsnode = np.random.randint(0, N)
        # get the spin of this node
        s = state[rsnode]
        # sum of all neighbouring spins
        ss = np.sum(state[top[rsnode].nonzero()[1]])
        # transition energy
        delE = 2.0 * J * ss * s
        # calculate transition probability
        prob = math.exp(-delE * beta)
        # conditionally accept the transition
        if prob > random.random():
            s = -s
        state[rsnode] = s
    
        return state
    

    注意过渡能量中的系数2.0-它在您的代码中丢失了!

    这里有一些numpy索引魔术。 top[i]是节点i的稀疏邻接行向量,而top[i].nonzero()[1]是非零元素的列索引(top[i].nonzero()[0]是行索引,因为它是行向量,所以它们都等于0)。 state[top[i].nonzero()[1]]因此是节点i的相邻节点的值。

    现在到物理。热力学性质是错误的,因为:

    e1 = e0 + ene * ene
    m1 = m0 + mag * mag
    

    确实应该是:

    e1 = e1 + ene * ene
    m1 = m1 + mag * mag
    

    和:

    specificheat.append( e1/steps - e0*e0/(steps*steps) /(N* t * t))
    susceptibility.append( m1/steps - m0*m0/(steps*steps) /(N* t *t))
    

    应该:

    specificheat.append((e1/steps/N - e0*e0/(steps*steps*N*N)) / (t * t))
    susceptibility.append((m1/steps/N - m0*m0/(steps*steps*N*N)) / t)
    

    (最好是尽早平均能量和磁化强度)

    这使热容量和磁化率达到正值。请注意分母中的单个t

    现在该程序是正确的(希望)正确了,让我们来谈谈物理学。对于每个温度,您都将从全自旋状态开始,然后让它一次演化一个自旋。显然,除非温度为零,否则此初始状态与热平衡相差甚远,因此系统将开始向状态空间中与给定温度对应的部分漂移。此过程称为热化,在此期间收集静态信息毫无意义。您必须始终将给定温度下的模拟分为两个部分-热化和实际运行。要达到平衡,需要进行多少次迭代?很难说-使用能量的移动平均值并监视何时(相对)稳定。

    其次,更新算法每次迭代更改一次自旋,这意味着程序将非常缓慢地探索状态空间,并且您将需要进行大量迭代才能获得分区函数的良好近似值。进行200次旋转后,1000次迭代可能就足够了。

    其余的问题确实不在这里。

    关于python - Ising模型[Python],我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43253150/

    10-12 20:08