美文网首页
算法_Simulated-Annealing_Python

算法_Simulated-Annealing_Python

作者: 计算士 | 来源:发表于2015-05-30 05:22 被阅读755次

    本文用到的包

    import random
    import numpy as np
    from numpy import linalg as LA
    import pylab as plt
    import networkx as nx
    import sys
    

    Simulated-Annealing (模拟退火) 是一种常见的优化方法。这里我们用它将一个任意维度的网络嵌入二维空间。为了检验算法效果,我们先制造一个二维网络A,从这个网络上我们可以得到任意两点之间的欧式距离。我们重新生成一个同等规模的随机网络C,利用C网络两点之间距离与A网络对应距离的差异,来促使C不断朝A演化,经过一千步得到B。可以看出效果非常好,几乎完全恢复了已有网络的结构。

    图1. 使用SA方法恢复网络结构图1. 使用SA方法恢复网络结构

    SA的思路来源于统计力学中的波尔兹曼分布。该分布假设,一个多粒子系统的态出现的概率与其总能量反比,同时这种反比关系受到温度约束。

    ![Eq. 1][1]
    [1]: http://latex.codecogs.com/svg.latex?P({state})\propto{e^{-\frac{E}{kT}}}

    上式中E代表系统能量,k是一个非常小的常数,T是系统的温度。我们在其他系统中,如果将要最小化的某个目标(往往是sum of squares)看做能量,则玻尔兹曼分布给我们建议了一种梯度下降的具体方法,因为从公式1我们可以推得

    ![Eq. 2][2]
    [2]: http://latex.codecogs.com/svg.latex?\frac{P({state2})}{P({state1})}=e^{\frac{E_1-E_2}{kT}}

    于是,在实际优化过程中,我们先可以对系统做一小调整,在本例中就是比照目标网络对初始网络中的节点位置进行微调,计算这个调整带来的“能量”差异,然后通过Eq.2来判断这个调整是否应该被接受。

    与死板地接受所有带来能量下降的调整的优化方法不同,模拟退火可以保证系统有一定的自由度:以小概率接受短期看不利,但长期可能有利的变化,使得系统不会被锁死在一个变化轨道上。所谓模拟退火,模拟指的就是早期保持较高的稳定,给系统较大的自由度接受短期不利的变化;后期降低温度,使得系统稳定地朝一个方向演化。

    将上述思想转化为Python函数如下:

    # -----------simulated annealing functions----------------------
    
    # refreshing results
    def flushPrint(variable):
        sys.stdout.write('\r')
        sys.stdout.write('%s' % variable)
        sys.stdout.flush()
        
    # calculate variance contributed by a node or all nodes 
    def varaince(newPos,Dis,node,nodePosition):
        if len(nodePosition) == 2:
            return sum([(LA.norm(newPos[j]-nodePosition)-Dis[(min(node,j),max(node,j))])**2 for j in newPos if j!=node])
        else:
            return 2*sum([(LA.norm(newPos[j]-newPos[i])-Dis[(i,j)])**2 for i,j in Dis])  
        
    def moveOneNode(newPos,Dis):
        k = 1.0/len(newPos) #factor to normalize force
        [i] = random.sample(newPos,1)
        newpos_i = np.array(list(newPos[i]))# prevent Python from connecting the value of newPos[i] with newpos_i
        varianceBefore = varaince(newPos,Dis,i,newpos_i)
        for j in newPos:
            newDis = LA.norm(newPos[j]-newpos_i)
            if j!=i and newDis!=0:
                oldDis = Dis[(min(i,j),max(i,j))]
                deltaDis = newDis - oldDis # magnitude and direction of the force j imposing on i
                newpos_i += (newPos[j]-newPos[i])*k*deltaDis/newDis # j pushes/pulls i
        varianceAfter = varaince(newPos,Dis,i,newpos_i)
        deltaE = varianceAfter-varianceBefore
        return i,newpos_i,deltaE
    
    def SA(newPos,Dis,Nsimulate,BoltzmanConstant,Temperature,coolingSpeed):
        v=varaince(newPos,Dis,"null","null")
        V=[v]
        for k in range(Nsimulate):
            flushPrint(k)
            i,newpos_i,deltaE = moveOneNode(newPos,Dis)
            if np.random.rand()<np.exp(-deltaE/(Temperature*BoltzmanConstant)):
                newPos[i] = newpos_i
                Temperature *= coolingSpeed 
                v += deltaE
                V.append(v)
        return newPos,V
    

    现在制造两个网络,一个用于作为优化目标,另一个作为初始状态

    # -----------initialization----------------
    # generating a random network in 2D space #
    N = 20
    Pos = dict((i,np.random.rand(2)) for i in range(N))
    Dis = {}
    E = nx.Graph()
    for i in Pos:
        for j in Pos:
            if i<j:
                dis = LA.norm(Pos[j]-Pos[i])
                Dis[(i,j)] = dis
                if dis < 0.4:
                    E.add_edge(i,j,weight=dis)
                    
    # generate new 2d nodes
    L = max(Dis.values()) #character length of the universe
    newPos = dict((i,np.random.rand(2)*L) for i in range(N))
    newPosCopy = newPos.copy() # copy it for plotting initial state
    

    本例中因为系统规模较小,可以设定参数进行如下演化

    newPos,V=SA(newPos,Dis,Nsimulate=1000, BoltzmanConstant=.01, Temperature=1, coolingSpeed = .9)
    

    先定义绘制网络的函数

    def plotG(ax,positions,edges):
        #nx,ny = zip(*nodes.values())
        for i in positions:
            xi,yi = positions[i]
            ax.scatter(xi,yi,s=300,facecolor='RoyalBlue',edgecolor='gray')
            ax.text(xi,yi,i,ha='center',va='center',size=10,color='white')
        for i,j in edges:
            x1,y1 = positions[i]
            x2,y2 = positions[j]
            ax.plot([x1,x2],[y1,y2],marker='',linestyle='-',color='gray',alpha=0.3)
    

    最后通过以下代码得到图1

    fig = plt.figure(figsize=(10, 10))
    ax1 = fig.add_subplot(221)
    plotG(ax1,Pos,E.edges())
    plt.text(.9,.9,'A',size=14)
    ax2 = fig.add_subplot(222)
    plt.text(.9,1.1,'B',size=14)
    plotG(ax2,newPos,E.edges())
    ax3 = fig.add_subplot(223)
    plt.text(.9,.9,'C',size=14)
    plotG(ax3,newPosCopy,E.edges())
    ax3 = fig.add_subplot(224)
    plt.text(900,37,'D',size=14)
    plt.plot(V,marker='.',linestyle='-',color='RoyalBlue')
    plt.xlabel('Simulation steps')
    plt.ylabel('Variance')
    #
    plt.tight_layout()
    plt.show()
    #plt.savefig('/Users/csid/Desktop/SA.png',transparent=True)
    

    相关文章

      网友评论

          本文标题:算法_Simulated-Annealing_Python

          本文链接:https://www.haomeiwen.com/subject/pnorqttx.html