Simulated Annealing Algorithm
Simulated Annealing with Constraints
The objective is to implement the simulated annealing algorithm. Indeed, for complete NP optimization problems, such as the problem of traveling salesman, we don't know a polynomial algorithm allowing an optimal resolution. We will therefore seek an approximate solution of this optimum using heuristics. Simulated annealing is an algorithm based on a heuristic allowing the search for a solution to a problem given. It allows in particular to avoid the local minima but requires an adjustment of its parameters. The simulated annealing algorithm can be represented as follows:
We set an initial vector configuration X Xopt = X Set an intial Temperature While (Temperature > Final Temperature and nbiter< itermax) do { Repetition=1 while (Repetition <= MaxTconst) do { Randomly choose Y in the vicinity of vector X DJ = J(Y ) - J(X) if DJ < 0 then { X = Y if J(X) < J(Xopt) then Xopt = X } else { Choose randomly p in [0, 1] if p <= exp(-DJ / T ) X = Y Repetition = Repetition + 1 } } nbiter=nbiter+1 T = g(T) } return Xopt
This algorithm have different parameters:
- The Initial Temperature: Tinit
- The Final Temperature: Tf
- The function g that enables the decrease of the temperature at each step of the algorithm
- The amplitude of the random choose in the vicinity of the current value of the state: Ampli
- The number of iterations at the same temperature: MaxTconst
At the beginning, the initial temperature is high so even if we choose a random value
-
Implement this algorithm in the following function:
def Recuit(Xinit, Tinit, Tf, Ampli, MaxTconst, itermax). This function returns a list: and all the iterative values of . We assume that function is equal to . This allows that the temperature decreases slowly. -
Apply this algorithm with the following functions:
def J(X): return (X[0]**2-4*X[0]+4)*(X[0]**2+4*X[0]+2) + X[1]**2 def g(T): return T**0.99
-
Tune the parameters such that you obtain the global minimum
.
def Recuit(Xinit, Tinit, Tf, Ampli, MaxTconst, itermax): j=1 X = Xinit T = Tinit L =[X] Xopt = X while(T > Tf and j < itermax): compteur = 1 while(compteur <= MaxTconst): Y=[] for i in range(len(X)): s=X[i]+Ampli*(-1.0+2.0*np.random.random_sample()) Y.append(s) DJ=J(Y)-J(X) if(DJ < 0.0): X = Y L.append(X) if(J(X) < J(Xopt)): Xopt = X else: p = np.random.random_sample() if(p <= np.exp(-DJ/T)): X = Y L.append(X) compteur = compteur + 1 T = g(T) j = j + 1 return [Xopt, L] res,x=Recuit([1],1000.0,1.0,0.5,2,100) print(x) print("xopt= ",res," true value= ",-1-np.sqrt(2))
Exercise [Simulated Annealing with two variables and constraints]
-
Implement this algorithm for the following function:
def J(X): return X[0]**2+X[1]**2
-
We wish to apply this algorithm to the following constraint problem:
such that Optimizing this problem with this algorithm is almost impossible because satisfying the constraint of at each step by choosing the variables and randomly such that is impossible. It's why we modify the equality constraint under the form of two inequalities in order to release this constraint:
and .
where parameter is a small value to be defined. This method also requires that the initial values verify the inequality constraints from the start. Write the Annealing function RecuitC which takes into account the inequality constraints.
def J(X): return X[0]**2+X[1]**2 def Recuit(Xinit, Tinit, Tf, Ampli, MaxTconst, itermax): j=1 X = Xinit T = Tinit L =[X] Xopt = X while(T > Tf and j < itermax): compteur = 1 while(compteur <= MaxTconst): Y=[] for i in range(len(X)): s=X[i]+Ampli*(-1.0+2.0*np.random.random_sample()) Y.append(s) DJ=J(Y)-J(X) if(DJ < 0.0): X = Y L.append(X) if(J(X) < J(Xopt)): Xopt = X else: p = np.random.random_sample() if(p <= np.exp(-DJ/T)): X = Y L.append(X) compteur = compteur + 1 T = g(T) j = j + 1 return [Xopt, L] res,X=Recuit([1,2],1000.0,1.0,0.5,2,200) print("xopt= ",res)
eps=0.1 def G(X): return [(X[0]-2)**2+X[1]**2-1-eps,-(X[0]-2)**2-X[1]**2+1-eps] def RecuitC(Xinit, Tinit, Tf, Ampli, MaxTconst, itermax): j=1 X = Xinit T = Tinit L =[X] Xopt = X while(T > Tf and j < itermax): compteur = 1 while(compteur <= MaxTconst): Y=[] for i in range(len(X)): s=X[i]+Ampli*(-1.0+2.0*np.random.random_sample()) Y.append(s) # Test if the Y vector satisfies all the constraints # if not we choose an another value if G(Y)[0] <=0 and G(Y)[1] <=0: # if G has more two constraints adapt the code DJ=J(Y)-J(X) if(DJ < 0.0): X = Y L.append(X) if (J(X) < J(Xopt)): Xopt = X else: p = np.random.random_sample() if(p <= np.exp(-DJ / T)): X = Y L.append(X) compteur = compteur + 1 T = g(T) j = j + 1 return [Xopt, L] # We choose an initial value which satisfies the constraint functions Xinit = [3,0] res=RecuitC(Xinit,1000.0,1.0,0.2,2,1500) print(res[0])
Exercise [Simulated Annealing for beam problem]
We want to determine the optimal section
mm
-
Find an optimal solution to this beam problem
Exercise [Simulated Annealing for transport problem]
Le problème de transport se présente de la manière suivante : on désire acheminer des marchandises de
Le critère représente le coût total de transport. Les contraintes expriment
l'égalité de l'offre et de la demande pour chaque point de vente et chaque dépôt.
On peut relaxer la contrainte on supposant que l’on souhaite au plus satisfaire la demande.
Les contraintes de type égalité sont aussi transformées en contraintes inégalité si

-
Tirer au hasard les points de production et les points associés aux clients.
Tracer ces points puis calculer les distances
entre les points et . Tirer au hasard les quantités produites de l'entrepôt ainsi que les valeurs des demandes de chaque client . On supposera que ces valeurs sont des entiers positifs. - Ecrire le problème d'optimisation avec inégalités
-
Intialiser les
en utilisant la méthode dite du coin Nord-Ouest - Résoudre ce problème avec le Recuit simulé sous contraintes
- Tracer la solution ainsi obtenue
Simulated Annealing and shortest path
In this Section, we adress the problem of finding the shortest paths between nodes in a graph,
which may represent, for example, road networks. For example, if the nodes of the graph represent cities
and edge path costs represent driving distances between pairs of cities connected by a direct road
(for simplicity, ignore red lights, stop signs, toll roads and other obstructions),
we want to define algorithms that can be used to find the shortest route between one city and all other cities.
A widely used application of shortest path algorithm is network routing protocols.
Let
We denote by
Therefore, we have to find the permutation which minimizes
For example, with
We therefore use a probabilistic algorithm called simulated annealing which will make it possible to answer the question in an approximate manner in a much shorter time even for large values of
To build this algorithm, we imagine that the function to be optimized is the energy of a fictitious physical system. At initialization, our physical system has an energy
We assume that the state associated with the initial energy
How to determine the random fluctuations of the energy of our system?
Statistical physics tells us that the probability of finding a system in an energy state
Let
Otherwise, we leave the system in the state
We generally choose an exponential decay law of the type
-
Define the Energy function, the initial state
of the system and the initial value of the temperature. -
In the case of the shortest path, the function to optimize is the total distance to be traveled which is the sum of the distances between each of the pairs of neighboring points. The state of the system is characterized by the path between the different points and the associated total distance which is represented by its energy. To change the system state, we simply swap the order of some points in the
list. -
To minimize the energy
we use the Metropolis algorithm (see below). The temperature is a control parameter with no particular functional significance. But the choice of the initial and final temperatures will constrain the efficiency of the algorithm. -
Depending on the number of points, we will have a lot of calculations. It is therefore appropriate to optimize the code.
-
Generate the list of points where the coordinates of the points
are drawn randomly according to the uniform law. The initial path is simply the indexing of the vector of the points according to their random coordinates initialized above. Save this initial path so that it can be displayed at the end of the calculation. -
We choose
, and .
The choice of the values of initial temperature and minimum temperature, that which fixes the end of the annealing, requires several tests. As a general rule, the value of must be of the same order of magnitude as the energy variation.
The value of must be chosen large enough, so that the decrease in temperature is slow enough, but not too much so as not to have too many calculations. These parameters can be modified to study their influence on the results.
Exercise [Simulated Annealing for the salesman problem]
-
Define function Energy() which calculates the sum of the distances between the points in the order of the path.
-
Define the Fluctuation() function which swaps the points in the path between index
and index . This simulates an energy fluctuation in the system. -
Implement the Metropolis algorithm:
""" The Metropolis function""" def Metropolis(E1,E2): global T,i,j # global variables if E1 <= E2: E2 = E1 # Energy of the new state else dE = E1-E2 if random.uniform() > exp(-dE/T): #The fluctuation is not accepted. #We then set the path in the previous state #by applying again the Fluctuation function Fluctuation(i,j) else: #The fluctuation is accepted E2 = E1 return E2
-
Code the main calculation loop of the simulated annealing algorithm. For this, you need to apply the Fluctuation(i,j) function where the indexes
and are drawn as random variables. Consequently, this function creates a new path with a new Energy. Apply the Metropolis function so as to accept or not this new state path. Decrease the temperature according to the following law: -
Draw the final path and the Energy history according to the temperature evolution. For this, you can use a logarithmic scale (see below).
import numpy as np
import matplotlib.pyplot as plt
""" The Energy function"""
def Energy():
s = 0.0
#TO DO
return s
""" The Fluctuation function"""
def Fluctuation(i,j):
#TO DO
""" The Metropolis function"""
def Metropolis(E1,E2):
#TO DO
return E2
# initialisation des listes d'historique
Henergie = [] # énergie
Htemps = [] # temps
HT = [] # température
N = 20 # number of cities or points
# paramètres de l'algorithme de recuit simulé
T0 = 100.0
Tmin = 1e-3
tau = 5000
x = random.uniform(size=N)
y = random.uniform(size=N)
# défintion du trajet initial : ordre croissant des villes
trajet = np.arange(N)
trajet_init = trajet.copy()
# calcul de l'énergie initiale du système (la distance initiale à minimiser)
Ec = Energy()
# boucle principale de l'algorithme de recuit simulé
t = 0
T = T0
while T > Tmin:
# TO DO
# historisation des données
if t % 10 == 0:
Henergie.append(Ec)
Htemps.append(t)
HT.append(T)
print("distance initiale= ",Henergie[0]," distance finale= ",Henergie[-1])
# affichage du réseau
fig1 = plt.figure(1)
plt.subplot(1,2,1)
plt.xticks([])
plt.yticks([])
plt.plot(x[trajet_init],y[trajet_init],'k')
plt.plot([x[trajet_init[-1]], x[trajet_init[0]]],[y[trajet_init[-1]],y[trajet_init[0]]],'k')
plt.plot(x,y,'ro')
plt.title('Initial Path')
plt.subplot(1,2,2)
plt.xticks([])
plt.yticks([])
plt.plot(x[trajet],y[trajet],'k')
plt.plot([x[trajet[-1]], x[trajet[0]]],[y[trajet[-1]], y[trajet[0]]],'k')
plt.plot(x,y,'ro')
plt.title('Optimal Path')
plt.show()
# affichage des courbes d'évolution
fig2 = plt.figure(2)
plt.subplot(1,2,1)
plt.semilogy(Htemps, Henergie)
plt.title("Total Energy Evolution")
plt.xlabel('Time')
plt.ylabel('Energy')
plt.subplot(1,2,2)
plt.semilogy(Htemps, HT)
plt.title('Temperature Evolution')
plt.xlabel('Time')
plt.ylabel('Temperature')
print("intial length = ",Henergie[0],"final length= ",Henergie[-1])


References
- D. P. Bertsekas (1999), Nonlinear Programming.
- J. Barzilai, J.M. Borwein (1988), Two-Point Step Size Gradient Methods, IMA Journal of Numerical Analysis (8), pp. 141-148.