raaraya commited on
Commit
7aab566
·
verified ·
1 Parent(s): c3ecff1

Upload 6 files

Browse files
Files changed (6) hide show
  1. Coulomb.jpg +0 -0
  2. F_coulomb.py +164 -0
  3. F_newton.py +152 -0
  4. Newton.jpg +0 -0
  5. Random.jpg +0 -0
  6. custom_pso.py +706 -0
Coulomb.jpg ADDED
F_coulomb.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ def normaliazed_direction(ori, des):
4
+ dir_ = des-ori
5
+ nor_dir_ = dir_/np.sqrt(np.sum(dir_**2))
6
+ nor_dir_=np.nan_to_num(nor_dir_)
7
+ return nor_dir_
8
+
9
+ def distance(ori, des):
10
+ dis_ = des-ori
11
+ return dis_
12
+
13
+ def acc(F, m):
14
+ Npar=F.shape[0]
15
+ Nvar=F.shape[1]
16
+ # Acceleration Matrix
17
+ Ac = np.full((Npar, Nvar), 0.0)
18
+ for ind_r, row in enumerate(Ac):
19
+ for ind_c, col in enumerate(row):
20
+ Ac[ind_r][ind_c]=F[ind_r][ind_c]/m[ind_r]
21
+ return Ac
22
+
23
+ def F_coulomb(x, v, q, P, minimize=True, k=0.0001, weight_method='Linear', t=1.0, max_q=100.0):
24
+ """ The procedure that generates the new positions of the particles is based on how particles with positive and negative charges move
25
+ in electric fields. Thus, the procedure will be as follows:
26
+
27
+ 1. n particles are created at random positions within the search space.
28
+ 2. Each particle is assigned a positive charge type.
29
+ 3. The magnitude of each particle's charge is directly related to the objective function.
30
+ 3.1 Assignment Methods -> Linear, Exponential, Quadratic (better assignment methods can be explored)
31
+ 3.2 Simply put, a solution range is established and adjusted to a charge range between 0 to max_q according to some assignment method.
32
+ 4. Each particle holds a velocity. These start at rest but will change over the iterations of the algorithm.
33
+ 5. The P particles with the best values obtained from the iteration will remain at rest and emit an electric field of opposite sign (negative) attracting the rest.
34
+ 5.1 Let E = k*Q/r^2 be the magnetic field at a point located at a distance r from the source.
35
+ 5.2 Let Fe be the electric force between the magnetic field and the particle -> Fe = E*q0 where q0 is the particle's charge.
36
+ 5.3 Let Fij be the force produced between particle i and j -> Fij = k*qi*qj/(r^2) where r is the distance between the particles, qi and qj are the charges of i and j.
37
+ 5.4 Thus, the resultant forces of particle i are FRi = sum_{p=1}^P k*Qp*Qi/(rip^2) + sum_{j=P+1}^n k*Qi*Qj/(rij^2)
38
+ 5.5 Moreover, by Newton's Law, F=m*a -> Fe=m*a. Thus, the velocities of each particle are calculated vf=vi+a*t -> vf = vi + (FRi/m)*t
39
+ 5.6 The positions are updated as follows x = vi*t + (a*t^2)/2 -> x = vi*t + (FRi/m)*(t^2)/2
40
+
41
+ Finally, if the particles are too close, it could cause a problem. If rij^2 << 0, then the electric force would be too strong, leading
42
+ the particles to abruptly separate from each other. To avoid this behavior, a small distance (0.00000001) is established. If the distance
43
+ between two particles is smaller than that distance, it will be considered that both particles have collided. Then, if more than half of
44
+ the particles have collided, we will apply a random function to generate new solutions over a reduced search space.
45
+
46
+ Args:
47
+ x (array(size=(Npar, Nvar)): current particle positions
48
+ v (array(size=(Npar, Nvar)): current particle velocity
49
+ q (array(size=(Npar, 1))): current electric charge of each particle
50
+ P (int): quantity of electric fields (the best P particles become electric fields)
51
+ minimize (bool, optional): solver objective. Defaults to True.
52
+ k (float, optional): electric constant. Defaults to 0.0001.
53
+ weight_method (str, optional): method to reassign electric charges. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
54
+ t (float, optional): time. Defaults to 1.0.
55
+ max_q (float, optional): upper bound of electric charge to assing. Defaults to 100.0.
56
+
57
+ Returns:
58
+ F_total, Ac, vf, new_pos: returns the force obtained on each particle, their acceleration, their velocities
59
+ and their new positions
60
+ """
61
+
62
+ Npar=x.shape[0]
63
+ Nvar=x.shape[1]
64
+
65
+ # Distance Matrix
66
+ dis = []
67
+ for ind_r, row in enumerate(x):
68
+ for ind_c, col in enumerate(x):
69
+ dis.append(distance(x[ind_r], x[ind_c]))
70
+ dis=np.array(dis).reshape((Npar,Npar,Nvar))
71
+
72
+ # Direction Matrix
73
+ d = []
74
+ for ind_r, row in enumerate(x):
75
+ for ind_c, col in enumerate(x):
76
+ d.append(normaliazed_direction(x[ind_r], x[ind_c]))
77
+ d=np.array(d).reshape((Npar,Npar,Nvar))
78
+
79
+ colisioned_part = []
80
+ r_2 = np.zeros((Npar, Npar))
81
+ for ind_r, row in enumerate(dis):
82
+ for ind_c, col in enumerate(dis):
83
+ value = dis[ind_r][ind_c]
84
+ value_2 = value**2
85
+ value_sum = np.sum(value_2)
86
+ if value_sum < 0.00000001: # Particles have practically collided. Notice later that fe=0.0 ahead.
87
+ r_2[ind_r][ind_c] = 0.0
88
+ if ind_r != ind_c:
89
+ colisioned_part.append(ind_c)
90
+ else:
91
+ r_2[ind_r][ind_c] = value_sum
92
+ colisioned_part_ = np.unique(np.array(colisioned_part))
93
+
94
+
95
+ # Each particle is assigned an electric charge magnitude based on the solution provided by the particle.
96
+ q=np.array(q)
97
+ min_value = q[0]
98
+ max_value = q[-1]
99
+ if minimize:
100
+ q = -1.0*(q-max_value-1.0)
101
+ max_value = q[0]
102
+
103
+ if weight_method=="Linear":
104
+ # We adjust the charges according to the following range [0, max_q].
105
+ q = (q/max_value)*max_q
106
+ reverse_ind = [len(q)-i-1 for i in range(len(q))] # It is inverted to give more charge to the particles that are further away.
107
+ q = q[reverse_ind]
108
+
109
+ elif weight_method=="Quadratic":
110
+ # We adjust the charges according to the following range [0, max_q].
111
+ q_2=q**2
112
+ q=(q_2/np.max(q_2))*max_q
113
+ reverse_ind = [len(q)-i-1 for i in range(len(q))] # It is inverted to give more charge to the particles that are further away.
114
+ q = q[reverse_ind]
115
+
116
+ elif weight_method=="Exponential":
117
+ # We adjust the charges according to the following range [0, max_q].
118
+ q_exp=np.array([np.exp(i) for i in q])
119
+ q=(q_exp/np.max(q_exp))*max_q
120
+ reverse_ind = [len(q)-i-1 for i in range(len(q))] # It is inverted to give more charge to the particles that are further away.
121
+ q = q[reverse_ind]
122
+
123
+ Npar=d.shape[0]
124
+ Nvar=d.shape[2]
125
+ F=np.full((Npar, Npar, Nvar), 0.0)
126
+ for ind_r, row in enumerate(dis):
127
+ for ind_c, col in enumerate(row):
128
+ # The magnitude of the electric force Fe is calculated.
129
+ d_2=r_2[ind_r][ind_c]
130
+ if d_2==0:
131
+ Fe=0.0
132
+ else:
133
+ q1=q[ind_r]
134
+ q2=q[ind_c]
135
+ Fe=float(k*q1*q2/d_2)
136
+ if ind_r >= P and ind_c >= P: # Repulsive forces are generated between particles of the same sign.
137
+ F[ind_r][ind_c]=-1.0*Fe*d[ind_r][ind_c]
138
+ else: # There is attraction between particles and electric fields.
139
+ F[ind_r][ind_c]=Fe*d[ind_r][ind_c]
140
+ F[:P, :P] = 0.0
141
+ F_total = np.sum(F, axis=1)
142
+ F_total[:P]=0.0
143
+ # Remember that F=ma -> m1*a1 = m2*a2. So, if m1>m2 -> a2>a1 -> Particles with greater mass have less acceleration.
144
+ # For this method, the weight of the particle is not important, so we will set them all to be equal to 1.0.
145
+ m=np.ones(Npar)
146
+ # vf = acc + vi*t
147
+ Ac=acc(F_total, m)
148
+ vf = Ac + t*v
149
+ Ac[:P]=0.0
150
+ vf[:P]=0.0 # The velocity of the magnetic fields is set to 0.0.
151
+ # Finally, Xf = xi + vi*t + 0.5*acc*(t^2) (Final Position)
152
+ x__=v*t + 0.5*Ac*(t**2)
153
+ new_pos = x + x__
154
+
155
+ # If more than half of the particles have collided, those that have collided will move randomly within the reduced search space.
156
+ max_crash_part=Npar/2
157
+ if len(colisioned_part_)>max_crash_part:
158
+ lo = np.min(new_pos, axis=0)
159
+ up = np.max(new_pos, axis=0)
160
+ for i in colisioned_part_:
161
+ new_pos[i] = np.random.uniform(low=lo, high=up, size=(1, Nvar))
162
+
163
+ return F_total, Ac, vf, new_pos
164
+
F_newton.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ def normaliazed_direction(ori, des):
4
+ dir_ = des-ori
5
+ nor_dir_ = dir_/np.sqrt(np.sum(dir_**2))
6
+ nor_dir_=np.nan_to_num(nor_dir_)
7
+ return nor_dir_
8
+
9
+ def distance(ori, des):
10
+ dis_ = des-ori
11
+ return dis_
12
+
13
+ def acc(F, m):
14
+ Npar=F.shape[0]
15
+ Nvar=F.shape[1]
16
+ # Matriz de aceleracion
17
+ Ac = np.full((Npar, Nvar), 0.0)
18
+ for ind_r, row in enumerate(Ac):
19
+ for ind_c, col in enumerate(row):
20
+ Ac[ind_r][ind_c]=F[ind_r][ind_c]/m[ind_r]
21
+ return Ac
22
+
23
+ def F_newton(x_, m, minimize=True, G=0.0001, keep_best=False, weight_method='Linear', t=1.0, max_mass=100.0):
24
+ """ The procedure that generates the new positions of the particles is based on how bodies are attracted to each other
25
+ through an attractive force (universal law of gravitation). Thus, the procedure will be as follows:
26
+
27
+ 1. n particles are created at random positions within the search space.
28
+ 2. Each particle is assigned a mass based on the solution provided by the particle.
29
+ 2.1 The better the solution provided by the particle, the greater the assigned mass.
30
+ 2.2 Assignment methods -> Linear, Exponential, Quadratic (further discussion on better assignment methods can be explored).
31
+ 2.2.1 In simple terms, a range of solutions is established and adjusted to a charge range between 0 to max_mass according to some assignment method.
32
+ 3. Let Fij be the force produced between particle i and j -> Fij = G*mi*mj/(r^2) where r is the distance between the particles, mi and mj are the masses of i and j.
33
+ 3.1 Thus, the resulting force on particle i is FRi = Σ{j=1} G*mi*mj/(rij^2).
34
+ 3.2 Additionally, by Newton's Law, F = m*a -> a = F/m. Thus, the velocities of each particle are calculated as vf = vi + a*t -> vf = vi + (F/m)*t.
35
+ 3.3 Finally, the positions are updated as follows: x' = x + vi*t + (a*t^2)/2 -> x' = x + vi*t + (F/m)*(t^2)/2.
36
+ 3.4 Applying the assumption vi=0 -> x' = x + (F/m)*(t^2)/2.
37
+
38
+ Args:
39
+ x_ (array(size=(Npar, Nvar)): current particle positions
40
+ m (array(size=(Npar, 1))): current mass of each particle
41
+ minimize (bool, optional): solver objective. Defaults to True.
42
+ G (float, optional): gravitational constant. Defaults to 0.0001.
43
+ keep_best (bool, optional): It will save the best value obtained in each iteration. Defaults to False.
44
+ weight_method (str, optional): method to reassign mass. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
45
+ t (float, optional): time. Defaults to 1.0.
46
+ max_mass (float, optional): upper bound of mass to assing. Defaults to 100.0.
47
+
48
+ Returns:
49
+ F_total, Ac, new_pos: returns the force obtained on each particle, their acceleration,
50
+ and their new positions
51
+ """
52
+ Npar=x_.shape[0]
53
+ Nvar=x_.shape[1]
54
+
55
+ # Distance Matrix
56
+ dis = []
57
+ for ind_r, row in enumerate(x_):
58
+ for ind_c, col in enumerate(x_):
59
+ dis.append(distance(x_[ind_r], x_[ind_c]))
60
+ dis=np.array(dis).reshape((Npar,Npar,Nvar))
61
+
62
+ # Direction Matrix
63
+ d = []
64
+ for ind_r, row in enumerate(x_):
65
+ for ind_c, col in enumerate(x_):
66
+ d.append(normaliazed_direction(x_[ind_r], x_[ind_c]))
67
+ d=np.array(d).reshape((Npar,Npar,Nvar))
68
+
69
+ colisioned_part = []
70
+ r_2 = np.zeros((Npar, Npar))
71
+ for ind_r, row in enumerate(dis):
72
+ for ind_c, col in enumerate(dis):
73
+ value = dis[ind_r][ind_c]
74
+ value_2 = value**2
75
+ value_sum = np.sum(value_2)
76
+ if value_sum < 0.00000001: # Particles have practically collided. Notice later that F_=0.0 ahead.
77
+ r_2[ind_r][ind_c] = 0.0
78
+ if ind_r != ind_c:
79
+ colisioned_part.append(ind_c)
80
+ else:
81
+ r_2[ind_r][ind_c] = value_sum
82
+ colisioned_part_ = np.unique(np.array(colisioned_part))
83
+
84
+
85
+ # Each particle is assigned a mass magnitude based on the solution provided by the particle.
86
+ m=np.array(m)
87
+ min_value = m[0]
88
+ max_value = m[-1]
89
+
90
+ if minimize:
91
+ m = -1.0*(m-max_value-1.0)
92
+ max_value = m[0]
93
+
94
+ if weight_method=="Linear":
95
+ # We adjust the mass according to the following range [0, max_mass].
96
+ m = (m/max_value)*max_mass
97
+ reverse_ind = [len(m)-i-1 for i in range(len(m))]
98
+ m = m[reverse_ind]
99
+
100
+ elif weight_method=="Quadratic":
101
+ # We adjust the mass according to the following range [0, max_mass].
102
+ m_2=m**2
103
+ m=(m_2/np.max(m_2))*max_mass
104
+ reverse_ind = [len(m)-i-1 for i in range(len(m))]
105
+ m = m[reverse_ind]
106
+
107
+ elif weight_method=="Exponential":
108
+ # We adjust the mass according to the following range [0, max_mass].
109
+ m_exp=np.array([np.exp(i) for i in m])
110
+ m=(m_exp/np.max(m_exp))*max_mass
111
+ reverse_ind = [len(m)-i-1 for i in range(len(m))]
112
+ m = m[reverse_ind]
113
+
114
+ m = np.nan_to_num(m, nan=0.0001)
115
+ Npar=d.shape[0]
116
+ Nvar=d.shape[2]
117
+ F=np.full((Npar, Npar, Nvar), 0.0)
118
+ for ind_r, row in enumerate(dis):
119
+ for ind_c, col in enumerate(row):
120
+ # The magnitude of the attraction force F_ is calculated.
121
+ d_2=r_2[ind_r][ind_c]
122
+ if d_2==0:
123
+ F_=0.0
124
+ else:
125
+ m1=m[ind_r]
126
+ m2=m[ind_c]
127
+ F_=float(G*m1*m2/d_2)
128
+ F[ind_r][ind_c]=F_*d[ind_r][ind_c]
129
+ F_total = np.sum(F, axis=1)
130
+ # Note: Between two particles, the attractive forces will be the same, but not their accelerations.
131
+ # Remember that F = M * A -> m1 * a1 = m2 * a2, so if m1 > m2 -> a2 > a1 -> Particles with greater mass have lower acceleration.Ac=acc(F_total, m)
132
+ Ac=acc(F_total, m)
133
+ Ac = np.nan_to_num(Ac, nan=0.0)
134
+
135
+ # Finally, Xf = xi + vi * t + 0.5 * acc * (t^2) (Final Position), but if the particles always start at rest (vi=0) -> xf = 0.5 * acc * (t^2).
136
+ x__ = 0.5*Ac*(t**2)
137
+ new_pos=x_ + x__
138
+
139
+ if keep_best==True:
140
+ best_ind = np.argmin(m)
141
+ Ac[best_ind]=0.0
142
+ new_pos=x_ + 0.5*Ac*(t**2)
143
+
144
+ # If more than half of the particles have collided, those that have collided will move randomly within the reduced search space.
145
+ max_crash_part=Npar/2
146
+ if len(colisioned_part_)>max_crash_part:
147
+ lo = np.min(new_pos, axis=0)
148
+ up = np.max(new_pos, axis=0)
149
+ for i in colisioned_part_:
150
+ new_pos[i] = np.random.uniform(low=lo, high=up, size=(1, Nvar))
151
+
152
+ return F_total, Ac, new_pos
Newton.jpg ADDED
Random.jpg ADDED
custom_pso.py ADDED
@@ -0,0 +1,706 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ --------------------------------------------------------------
3
+ Custom PSO algorithm for continuous domains
4
+ --------------------------------------------------------------
5
+ Autor: Rodrigo Araya
6
7
+ '''
8
+
9
+ import math
10
+ import numpy as np
11
+ import pandas as pd
12
+ from F_newton import F_newton
13
+ from F_coulomb import F_coulomb
14
+ import plotly.graph_objects as go
15
+ import plotly.express as px
16
+
17
+
18
+ def Random_(problem, x_, auto_reduce_search_space=False):
19
+ """ This solver generates a random solution for each particle. Additionally, it is also possible to
20
+ apply an exploitation method where the search space is reduced on each iteration.
21
+
22
+ Args:
23
+ problem (dic): Dictionary that include: objective function, lower bound and upper bound of each variable and optimal solution
24
+ x_ (array(size=())): Current position of each particle
25
+ auto_reduce_search_space (bool, optional): If True, it update the lower and upper bounds in a way to reduce the search space. Defaults to False.
26
+
27
+ Returns:
28
+ Stemp, S_f: New positions for each particle and their performance after being evaluated with the objective function.
29
+ """
30
+
31
+
32
+ nPart = x_.shape[0]
33
+ nVar = x_.shape[1]
34
+
35
+ if auto_reduce_search_space:
36
+ Lo = np.min(x_, axis=0)
37
+ Up = np.max(x_, axis=0)
38
+ else:
39
+ Lo = np.zeros(nVar)
40
+ Up = np.ones(nVar)
41
+
42
+ new_x = np.random.uniform(low=Lo, high=Up, size=(nPart, nVar))
43
+
44
+ Stemp = np.zeros((nPart,nVar))
45
+ for k in range(nPart):
46
+ for i in range(nVar):
47
+ Stemp[k][i] = new_x[k][i]
48
+ if Stemp[k][i] > Up[i]:
49
+ Stemp[k][i] = Up[i]
50
+ elif Stemp[k][i] < Lo[i]:
51
+ Stemp[k][i] = Lo[i]
52
+ f,S_r,maximize = mp_evaluator(problem, Stemp)
53
+
54
+ S_f = np.zeros((nPart,1))
55
+
56
+ for i in range(len(S_r)):
57
+ S_f[i] = f[i]
58
+ return Stemp, S_f
59
+
60
+ def F_newton_(problem, x_, m, Lo, Up, G=0.00001, keep_best=True, weight_method='Linear', t=1.0, max_mass=100.0):
61
+ _, _, new_x = F_newton(x_, m, minimize=True, G=G, keep_best=keep_best, weight_method=weight_method, t=t, max_mass=max_mass)
62
+ nPart=x_.shape[0]
63
+ nVar=x_.shape[1]
64
+ Stemp = np.zeros((nPart,nVar))
65
+ for k in range(nPart):
66
+ for i in range(nVar):
67
+ Stemp[k][i] = new_x[k][i]
68
+ if Stemp[k][i] > Up[i]:
69
+ Stemp[k][i] = Up[i]
70
+ elif Stemp[k][i] < Lo[i]:
71
+ Stemp[k][i] = Lo[i]
72
+ f,S_r,maximize = mp_evaluator(problem, Stemp)
73
+
74
+ S_f = np.zeros((nPart,1))
75
+
76
+ for i in range(len(S_r)):
77
+ S_f[i] = f[i]
78
+ return Stemp, S_f
79
+
80
+ def F_coulomb_(problem, x_, v, q, P, Lo, Up, k=0.00001, weight_method='Linear', t=1.0, max_q=100.0):
81
+ _, _, v, new_x = F_coulomb(x_, v, q, P=P, minimize=True, k=k, weight_method=weight_method, t=t, max_q=max_q)
82
+ nPart=x_.shape[0]
83
+ nVar=x_.shape[1]
84
+ Stemp = np.zeros((nPart,nVar))
85
+ for k in range(nPart):
86
+ for i in range(nVar):
87
+ Stemp[k][i] = new_x[k][i]
88
+ if Stemp[k][i] > Up[i]:
89
+ Stemp[k][i] = Up[i]
90
+ elif Stemp[k][i] < Lo[i]:
91
+ Stemp[k][i] = Lo[i]
92
+ f,S_r,maximize = mp_evaluator(problem, Stemp)
93
+
94
+ S_f = np.zeros((nPart,1))
95
+
96
+ for i in range(len(S_r)):
97
+ S_f[i] = f[i]
98
+ return Stemp, v, S_f
99
+
100
+ def evaluator(problem, x):
101
+ # Dado que se trabaja bajo el dominio [0, 1] para cada variable, se debe crear una funcion que regrese a los valores a los dominios originales
102
+ # Ejemplo (ndim=2): si se tiene el dominio original [-5, 5] y [-2, 2] para la variables x1, x2 y se cambio a [0, 1] y [0, 1] se vuelve al original tras:
103
+ # y1=ax+b -> -5 = a*0+b -> b=-5
104
+ # y1=ax+b -> 5 = a*1+-5 -> a=10
105
+ # y1=10*x+-5
106
+ # y2=ax+b -> -2 = a*0+b -> b=-2
107
+ # y2=ax+b -> 2 = a*1+-2 -> a=4
108
+ # y2=4*x+-2
109
+ # Luego se aplica y1(x1) e y2(x2) respectivamente. Extendible a n dimensiones.
110
+ # Dado que [0, 1] no cambia, se generaliza la formula b=lb y a=ub-lb -> y_{i} = (ub-lb)_{i}*x + lb_{i}
111
+ lb = [var[0] for var in problem['bounds']]
112
+ ub = [var[1] for var in problem['bounds']]
113
+ x = [(ub[ind]-lb[ind])*i+lb[ind] for ind, i in enumerate(x)]
114
+ # calculate fitness
115
+ f = problem['f'](x)
116
+ fitness = dict(Obj=f)
117
+ return fitness
118
+
119
+ def mp_evaluator(problem, x):
120
+ results = [evaluator(problem, c) for c in x]
121
+ f = [r['Obj'] for r in results]
122
+ # maximization or minimization problem
123
+ maximize = False
124
+ return (f, [r for r in results],maximize)
125
+
126
+ def correct_x(problem, x_):
127
+ lb = [var[0] for var in problem['bounds']]
128
+ ub = [var[1] for var in problem['bounds']]
129
+ x = np.empty(x_.shape)
130
+ for ind, row in enumerate(x_):
131
+ corr_row = np.array([(ub[ind]-lb[ind])*i+lb[ind] for ind, i in enumerate(row)])
132
+ x[ind]=corr_row
133
+ return x
134
+
135
+
136
+
137
+ def custom_pso(problem, nPart, nVar, maxiter, G=0.00001, k=0.0001, keep_best=True, weight_method='Linear', t=1.0, seed=0, max_mass=100.0, solver='Newton', P=3, max_q=100.0, auto_reduce_search_space=False, dinamic=False):
138
+ """The procedure of this algorithm is as follows:
139
+ 1. Generate a file of size nSize=nPart.
140
+ 2. Generate nPart initial solutions. Each solution contains nVar variables.
141
+ 3. Evaluate the nPart solutions and add them to the file ordered from best to worst solution.
142
+ 4. While the termination condition is not met (iterations > maxiter):
143
+ 4.1 Generate new solutions using some solver. Options = ['Random', 'Newton', 'Coulomb']
144
+ 4.2 Add new solutions to the file.
145
+ 4.3 Remove duplicate solutions.
146
+ 4.4 Evaluate solutions and sort from best to worst solution.
147
+ 4.5 Keep in the file the nPar best solutions.
148
+ 4.6 Save iteration results in a history (dataframe).
149
+ 4.7 Evaluate termination condition. If negative, return to step 4.1.
150
+
151
+ Args:
152
+ problem (dic): Dictionary that include: objective function, lower bound and upper bound of each variable and optimal solution
153
+ nPart (_type_): Quantity of particles
154
+ nVar (_type_): Quantity of variables
155
+ maxiter (_type_): Maximum number of iterations.
156
+ seed (int, optional): set the generation of random numbers. Defaults to 0.
157
+ solver (str, optional): solver to apply. Defaults to 'Newton'. Options=['Random', 'Newton', 'Coulomb'].
158
+
159
+ Random Solver:
160
+ auto_reduce_search_space (bool, optional): If True, it update the lower and upper bounds in a way to reduce the search space. Defaults to False.
161
+
162
+ Newton Solver:
163
+ G (float, optional): Gravitational constant. Defaults to 0.00001.
164
+ keep_best (bool, optional): It will keep the best value obtained in each iteration. Defaults to True.
165
+ weight_method (str, optional): method to reassign particle mass. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
166
+ t (float, optional): time. Defaults to 1.0.
167
+ max_mass (float, optional): upper bound of mass to assing. Defaults to 100.0.
168
+ dinamic (bool, optional): It will change the max_mass value depending on the current iteration and difference between best and worst value obteined. Defaults to False.
169
+
170
+ Coulomb Solver:
171
+ P (int): quantity of electric fields (the best P particles become electric fields)
172
+ max_q (float, optional): upper bound of electric charge to assing. Defaults to 100.0.
173
+ weight_method (str, optional): method to reassign electric charges. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
174
+ k (float, optional): electric constant. Defaults to 0.0001.
175
+ t (float, optional): time. Defaults to 1.0.
176
+ dinamic (bool, optional): It will change the max_q value depending on the current iteration and difference between best and worst value obteined. Defaults to False.
177
+
178
+
179
+ Returns:
180
+ df, best_var, best_sol: dataframe contening data from all iterations, the best variables and the best value obtained
181
+ """
182
+
183
+ # number of variables
184
+ parameters_v = [f'x{i+1}' for i in range(nVar)]
185
+
186
+ # number of variables
187
+ nVar = len(parameters_v)
188
+
189
+ # size of solution archive
190
+ nSize = nPart
191
+
192
+ # number of Particules
193
+ nPart = nPart
194
+
195
+ # maximum iterations
196
+ maxiter = maxiter
197
+
198
+ # bounds of variables
199
+ Up = [1]*nVar
200
+ Lo = [0]*nVar
201
+
202
+ # dinamic q
203
+ din_max_q=max_q
204
+
205
+ # dinamic mass
206
+ din_max_mass = max_mass
207
+
208
+ # initilize matrices
209
+ S = np.zeros((nSize,nVar))
210
+ S_f = np.zeros((nSize,1))
211
+
212
+ # initial velocity
213
+ v = np.zeros((nSize,nVar))
214
+
215
+ # history
216
+ columns_ = ['iter']
217
+ for i in parameters_v: columns_.append(i)
218
+ columns_.append('f')
219
+ df = pd.DataFrame(columns=columns_)
220
+
221
+ # generate first random solution
222
+ #np.random.seed(seed)
223
+ Srand = np.random.uniform(low=0,high=1,size=(nPart, nVar))
224
+ f,S_r,maximize = mp_evaluator(problem, Srand)
225
+
226
+
227
+ for i in range(len(S_r)):
228
+ S_f[i] = f[i]
229
+
230
+ # add responses and "fitness" column to solution
231
+ S = np.hstack((Srand, v, S_f))
232
+ # sort according to fitness (last column)
233
+ S = sorted(S, key=lambda row: row[-1],reverse = maximize)
234
+ S = np.array(S)
235
+
236
+ # save each iteration
237
+ iter_n = np.full((nPart, 1), 0.0)
238
+ x_ = S[:, 0:nVar]
239
+ x = correct_x(problem, x_)
240
+ res = np.reshape(S[:, -1], (nPart, 1))
241
+ rows=np.hstack((iter_n, x, res))
242
+ df_new = pd.DataFrame(rows, columns=columns_)
243
+ df = pd.concat([df, df_new])
244
+
245
+ iterations=1
246
+
247
+ # iterations
248
+ while True:
249
+ # get a new solution
250
+ if solver == "Random":
251
+ Stemp, S_f = Random_(problem, S[:, :nVar], auto_reduce_search_space=auto_reduce_search_space)
252
+
253
+ elif solver=="Newton":
254
+ din_mass=0
255
+ if dinamic:
256
+ din_mass = ((maxiter-iterations)/(maxiter))*(np.max(S_f)-np.min(S_f))
257
+ else:
258
+ din_mass=max_mass
259
+
260
+ m = np.reshape(S[:, -1], (nPart, 1))
261
+ Stemp, S_f = F_newton_(problem, S[:, :nVar], m, Lo, Up, G=G, keep_best=keep_best, weight_method=weight_method, t=t, max_mass=din_mass)
262
+
263
+ elif solver == "Coulomb":
264
+ din_q=0
265
+ if dinamic:
266
+ din_q = ((maxiter-iterations)/(maxiter))*(np.max(S_f)-np.min(S_f))
267
+ else:
268
+ din_q=max_q
269
+ q = np.reshape(S[:, -1], (nPart, 1))
270
+ Stemp, v, S_f = F_coulomb_(problem, S[:, :nVar], v, q, P, Lo, Up, k=k, weight_method=weight_method, t=t, max_q=din_q)
271
+
272
+ # add responses and "fitness" column to solution
273
+ Ssample = np.hstack((Stemp, v, S_f))
274
+
275
+ # add new solutions in the solutions table
276
+ Solution_temp = np.vstack((S,Ssample))
277
+
278
+ # delate duplicated rows
279
+ Solution_temp = np.unique(Solution_temp, axis=0)
280
+
281
+ # sort according to "fitness"
282
+ Solution_temp = sorted(Solution_temp, key=lambda row: row[-1],reverse = maximize)
283
+ Solution_temp = np.array(Solution_temp)
284
+
285
+ # keep best solutions
286
+ S = Solution_temp[:nSize][:]
287
+
288
+ # save each iteration
289
+ iter_n = np.full((nPart, 1), iterations)
290
+ x_ = S[:, 0:nVar]
291
+ x = correct_x(problem, x_)
292
+ res = np.reshape(S[:, -1], (nPart, 1))
293
+ rows=np.hstack((iter_n, x, res))
294
+ df_new = pd.DataFrame(rows, columns=columns_)
295
+ df = pd.concat([df, df_new])
296
+
297
+ iterations += 1
298
+ if iterations > maxiter:
299
+ break
300
+
301
+ best_sol = np.min(df['f'])
302
+ ind_best_sol = np.argmin(df['f'])
303
+ best_var = df.iloc[ind_best_sol, 1:len(parameters_v)+1]
304
+ return df, best_var, best_sol
305
+
306
+ # Test Functions.
307
+ # Adapted from "https://www.sfu.ca/~ssurjano/optimization.html"
308
+
309
+ def Ackley(x, a=20.0, b=0.2, c=2.0*np.pi):
310
+ d = len(x)
311
+ sum1 = np.sum(np.square(x))
312
+ sum2 = np.sum(np.array([np.cos(c*i) for i in x]))
313
+ term1 = -a * np.exp(-b * np.sqrt(sum1 / d))
314
+ term2 = -np.exp(sum2 / d)
315
+ return term1 + term2 + a + np.exp(1)
316
+
317
+ def sixth_Bukin(x):
318
+ return 100 * np.sqrt(np.abs(x[1] - 0.01*x[0]**2)) + 0.01*np.abs(x[0] + 10)
319
+
320
+ def Cross_in_Tray(x):
321
+ return -0.0001 * math.pow(np.abs(np.sin(x[0]) * np.sin(x[1]) * np.exp(np.abs(100.0 - np.sqrt(x[0]**2 + x[1]**2)/np.pi)))+1.0, 0.1)
322
+
323
+ def Drop_Wave(x):
324
+ return -1.0*(1.0 + np.cos(12.0 * np.sqrt(x[0]**2 + x[1]**2))) / (0.5 * (x[0]**2 + x[1]**2) + 2.0)
325
+
326
+ def Eggholder(x):
327
+ return -(x[1] + 47.0) * np.sin(np.sqrt(np.abs(x[1] + x[0]/2 + 47.0))) - x[0] * np.sin(np.sqrt(np.abs(x[0] - (x[1] + 47.0))))
328
+
329
+ def Griewank(x):
330
+ sum_part=0.0
331
+ prod_part=1.0
332
+ for i, xi in enumerate(x):
333
+ sum_part += xi/4000.0
334
+ prod_part *= np.cos(xi/np.sqrt(i+1))
335
+ return sum_part - prod_part + 1.0
336
+
337
+ def Holder_Table(x):
338
+ return -np.abs(np.sin(x[0])*np.cos(x[1])*np.exp(np.abs(1.0-(np.sqrt(x[0]**2 + x[1]**2)/np.pi))))
339
+
340
+ def Levy(x):
341
+ # d dimensions
342
+ w1 = 1.0 + (x[0]-1.0)/4.0
343
+ wd = 1.0 + (x[-1]-1.0)/4.0
344
+ sum_part = 0.0
345
+ for i, xi in enumerate(x):
346
+ wi = 1.0 + (xi-1.0)/4.0
347
+ sum_part += ((wi-1.0)**2)*(1.0 + 10.0*math.pow(np.sin(np.pi*wi+1.0), 2))
348
+ return math.pow(np.sin(np.pi*w1), 2) + sum_part + ((wd-1.0)**2)*(1.0 + math.pow(np.sin(2*np.pi*wd), 2))
349
+
350
+ def Rastrigin(x):
351
+ # d dimensions
352
+ d = len(x)
353
+ sum_part = 0.0
354
+ for i, xi in enumerate(x):
355
+ sum_part += (xi**2) - 10.0*np.cos(2*np.pi*xi)
356
+ return 10*d + sum_part
357
+
358
+ def second_Schaffe(x):
359
+ return 0.5 + (math.pow(np.sin(x[0]**2 + x[1]**2), 2)-0.5)/(1.0 + 0.001*(x[0]**2 + x[1]**2))**2
360
+
361
+ def fourth_Schaffer(x):
362
+ return 0.5 + (math.pow(np.cos(np.abs(x[0]**2 - x[1]**2)), 2)-0.5)/(1.0 + 0.001*(x[0]**2 + x[1]**2))**2
363
+
364
+ def Schwefel(x):
365
+ # d dimensions
366
+ d = len(x)
367
+ sum_part = 0.0
368
+ for xi in x:
369
+ sum_part += xi*np.sin(np.sqrt(np.abs(xi)))
370
+ return 418.9829*d - sum_part
371
+
372
+ def Shubert(x):
373
+ sum_1 = 0.0
374
+ sum_2 = 0.0
375
+ for i in np.arange(5):
376
+ i = float(i + 1)
377
+ sum_1 += i*np.cos((i+1)*x[0] + i)
378
+ sum_2 += i*np.cos((i+1)*x[1] + i)
379
+ return sum_1*sum_2
380
+
381
+ def Styblinski_Tang(x):
382
+ f = (sum([math.pow(i,4)-16*math.pow(i,2)+5*i for i in x])/2)
383
+ return f
384
+
385
+ def Easom(x):
386
+ f = np.cos(x[0])*np.cos(x[1])*np.exp(-(math.pow(x[0]-np.pi, 2) + math.pow(x[1]-np.pi, 2)))
387
+ return f
388
+
389
+ def Bohachevsky(x):
390
+ f = x[0]**2 + 2*x[1]**2 -0.3*np.cos(3*np.pi*x[0]) - 0.4*np.cos(4*np.pi*x[1]) + 0.7
391
+ return f
392
+
393
+ def Perm_d_beta(x, d=2, beta=4):
394
+ f = 0.0
395
+ for i in range(d):
396
+ for j in range(d):
397
+ f += ((j+1) + beta)*(x[j]**(i+1) - (1/(j+1)**(i+1)))**2
398
+ return f
399
+
400
+ def Rotated_Hyper_Ellipsoid(x, d=2):
401
+ f = 0.0
402
+ for i in range(d):
403
+ for j in range(i+1):
404
+ f += x[j]**2
405
+ return f
406
+
407
+ def Sum_of_Different_Powers(x, d=2):
408
+ f = 0.0
409
+ for i in range(d):
410
+ f += np.abs(x[i])**i+1+1
411
+ return f
412
+
413
+ def SUM_SQUARES(x, d=2):
414
+ f = 0.0
415
+ for i in range(d):
416
+ f += (i+1)*x[i]**2
417
+ return f
418
+
419
+ def TRID(x, d=2):
420
+ sum_1 = 0.0
421
+ sum_2 = 0.0
422
+ for i in range(d):
423
+ sum_1 += (x[i] - 1)**2
424
+ for i in range(d-1):
425
+ sum_2 += x[i+1]*x[i]
426
+ f = sum_1 + sum_2
427
+ return f
428
+
429
+ def BOOTH(x):
430
+ f = (x[0] + 2*x[1] -7)**2 + (2*x[0] + x[1] - 5)**2
431
+ return f
432
+
433
+ def Matyas(x):
434
+ f = 0.26*(x[0]**2 + x[1]**2) - 0.48*x[0]*x[1]
435
+ return f
436
+
437
+ def MCCORMICK(x):
438
+ f = np.sin(x[0] + x[1]) + (x[0] - x[1])**2 - 1.5*x[0] + 2.5*x[1] + 1
439
+ return f
440
+
441
+ def Power_Sum(x, d=2, b=[8, 18, 44, 114]):
442
+ f = 0.0
443
+ for i in range(d):
444
+ sum_1 = 0.0
445
+ for j in range(d):
446
+ sum_1 += x[j]**(i+1)
447
+ f += (sum_1 - b[i])**2
448
+ return f
449
+
450
+ def Zakharov(x, d=2):
451
+ f = 0.0
452
+ sum_1 = 0.0
453
+ sum_2 = 0.0
454
+ sum_3 = 0.0
455
+ for i in range(d):
456
+ sum_1 += x[i]**2
457
+ sum_2 += 0.5*(i+1)*x[i]
458
+ sum_3 += 0.5*(i+1)*x[i]
459
+ f = sum_1 + sum_2**2 + sum_3**4
460
+ return f
461
+
462
+ def THREE_HUMP_CAMEL(x):
463
+ f = 2*x[0]**2 - 1.05*x[0]**4 + (x[0]**6/6) + x[0]*x[1] + x[1]**2
464
+ return f
465
+
466
+ def SIX_HUMP_CAMEL(x):
467
+ f = (4 - 2.1*x[0] + (x[0]**4)/3)*x[0]**2 + x[0]*x[1] + (-4 + 4*x[1]**2)*x[1]**2
468
+ return f
469
+
470
+ def DIXON_PRICE(x, d=2):
471
+ sum_1 = 0.0
472
+ for i in range(d-1):
473
+ i = i + 1
474
+ sum_1 += (i+1)*(2*x[i]**2 - x[i-1])**2
475
+ f = (x[0] - 1)**2 + sum_1
476
+ return f
477
+
478
+ def ROSENBROCK(x, d=2):
479
+ f = 0.0
480
+ for i in range(d-1):
481
+ f += 100*(x[i+1] - x[i]**2)**2 + (x[i] - 1)**2
482
+ return f
483
+
484
+ def DE_JONG(x):
485
+ a = [[-32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32, -32, -16, 0, 16, 32],
486
+ [-32, -32, -32, -32, -32, -16, -16, -16, -16, -16, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 32, 32, 32, 32, 32]]
487
+ sum_1 = 0.0
488
+ for i in range(25):
489
+ sum_1 += 1/((i+1) + (x[0] - a[0][i])**6 + (x[1] - a[1][i])**6)
490
+ f = (0.002 + sum_1)**(-1)
491
+ return f
492
+
493
+ def MICHALEWICZ(x, d=2, m=10):
494
+ f = 0.0
495
+ for i in range(d):
496
+ f += np.sin(x[i])*np.sin(((i+1)*x[i]**2)/np.pi)**(2*m)
497
+ f = -f
498
+ return f
499
+
500
+ def BEALE(x):
501
+ f = (1.5 - x[0] + x[0]*x[1])**2 + (2.25 - x[0] + x[0]*x[1]**2)**2 + (2.625 - x[0] + x[0]*x[1]**3)**2
502
+ return f
503
+
504
+ def BRANIN(x):
505
+ a=1
506
+ b=5.1/(4*(np.pi)**2)
507
+ c = 5/np.pi
508
+ r = 6
509
+ s = 10
510
+ t = 1/(8*np.pi)
511
+ f = a*(x[1] - b*x[0]**2 + c*x[0] - r)**2 + s*(1 - t)*np.cos(x[0]) + s
512
+ return f
513
+
514
+ def GOLDSTEIN_PRICE(x):
515
+ f = (1 + ((x[0] + x[1] + 1)**2)*(19 - 14*x[0] + 3*x[0]**2 - 14*x[1] + 6*x[0]*x[1] + 3*x[1]**2))*(30 + ((2*x[0] - 3*x[1])**2)*(18 - 32*x[0] + 12*x[0]**2 + 48*x[1] - 36*x[0]*x[1] + 27*x[1]**2))
516
+ return f
517
+
518
+ def PERM_D_BETA(x, d=2, beta=1):
519
+ f = 0.0
520
+ for i in range(d):
521
+ sum_1 = 0
522
+ for j in range(d):
523
+ sum_1 += ((j+1)**(i+1) + beta)*((x[j]/(j+1))**(i+1) - 1)
524
+ f += sum_1
525
+ return f
526
+
527
+ class test_functions():
528
+ def __init__(self) -> None:
529
+ self.Ackley_ = {'name':'Ackley', 'f':Ackley, 'bounds':[[-32.768, 32.768], [-32.768, 32.768]], 'opt':[[0.0, 0.0], 0.0]}
530
+ self.sixth_Bukin_ = {'name':'sixth_Bukin', 'f':sixth_Bukin, 'bounds':[[-15.0, -5.0], [-3.0, 3.0]], 'opt':[[-10.0, 1.0], 0.0]}
531
+ self.Cross_in_Tray_ = {'name':'Cross_in_Tray', 'f':Cross_in_Tray, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[[1.3491, -1.3491], [1.3491, 1.3491], [-1.3491, 1.3491], [-1.3491, -1.3491]], -2.06261]}
532
+ self.Drop_Wave_ = {'name':'Drop_Wave', 'f':Drop_Wave, 'bounds':[[-5.12, 5.12], [-5.12, 5.12]], 'opt':[[0, 0], -1.0]}
533
+ self.Eggholder_ = {'name':'Eggholder', 'f':Eggholder, 'bounds':[[-512.0, 512.0], [-512.0, 512.0]], 'opt':[[512.404, 512.404], -959.6407]}
534
+ self.Griewank_ = {'name':'Griewank', 'f':Griewank, 'bounds':[[-600.0, 600.0], [-600.0, 600.0]], 'opt':[[0.0, 0.0], 0.0]}
535
+ self.Holder_Table_ = {'name':'Holder_Table', 'f':Holder_Table, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[[8.05502, 9.66459], [8.05502, -9.66459], [-8.05502, 9.66459], [-8.05502, -9.66459]], -19.2085]}
536
+ self.Levy_ = {'name':'Levy', 'f':Levy, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[1.0, 1.0], 0.0]}
537
+ self.Rastrigin_ = {'name':'Rastrigin', 'f':Rastrigin, 'bounds':[[-5.12, 5.12], [-5.12, 5.12]], 'opt':[[0.0, 0.0], 0.0]}
538
+ self.second_Schaffe_ = {'name':'second_Schaffe', 'f':second_Schaffe, 'bounds':[[-100.0, 100.0], [-100.0, 100.0]], 'opt':[[0.0, 0.0], 0.0]}
539
+ self.fourth_Schaffer_ = {'name':'fourth_Schaffer', 'f':fourth_Schaffer, 'bounds':[[-100.0, 100.0], [-100.0, 100.0]], 'opt':[[0.0, 0.0], 0.0]}
540
+ self.Schwefel_ = {'name':'Schwefel', 'f':Schwefel, 'bounds':[[-500.0, 500.0], [-500.0, 500.0]], 'opt':[[420.9687, 420.9687], 0.0]}
541
+ self.Shubert_ = {'name':'Shubert', 'f':Shubert, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[0.0, 0.0], -186.7309]}
542
+ self.Styblinski_Tang_ = {'name':'Styblinski_Tang', 'f':Styblinski_Tang, 'bounds':[[-5, 5], [-5, 5]]}
543
+ self.Easom_ = {'name':'Easom', 'f':Easom, 'bounds':[[-3, 3], [-3, 3]]}
544
+ self.Bohachevsky_ = {'name':'Bohachevsky', 'f':Bohachevsky, 'bounds':[[-100.0, 100.0], [-100.0, 100.0]], 'opt':[[0, 0], 0]}
545
+ self.Perm_d_beta_ = {'name':'Perm_d_beta', 'f':Perm_d_beta, 'bounds':[[-2.0, 2.0], [-2.0, 2.0]], 'opt':[[1, 0.5], 0]}
546
+ self.Rotated_Hyper_Ellipsoid_ = {'name':'Rotated_Hyper_Ellipsoid', 'f':Rotated_Hyper_Ellipsoid, 'bounds':[[-65.536, 65.536], [-65.536, 65.536]], 'opt':[[0.0, 0.0], 0]}
547
+ self.Sum_of_Different_Powers_ = {'name':'Sum_of_Different_Powers', 'f':Sum_of_Different_Powers, 'bounds':[[-1, 1], [-1, 1]], 'opt':[[0.0, 0.0], 0]}
548
+ self.SUM_SQUARES_ = {'name':'SUM_SQUARES', 'f':SUM_SQUARES, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[0.0, 0.0], 0]}
549
+ self.TRID_ = {'name':'TRID', 'f':TRID, 'bounds':[[-4, 4], [-4, 4]], 'opt':[[2, 2], -2]}
550
+ self.BOOTH_ = {'name':'BOOTH', 'f':BOOTH, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[1, 3], 0]}
551
+ self.Matyas_ = {'name':'Matyas', 'f':Matyas, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[0, 0], 0]}
552
+ self.MCCORMICK_ = {'name':'MCCORMICK', 'f':MCCORMICK, 'bounds':[[-1.5, 4], [-3, 4]], 'opt':[[-0.54719, -1.54719], -1.9133]}
553
+ self.Power_Sum_ = {'name':'Power_Sum', 'f':Power_Sum, 'bounds':[[0, 2], [0, 2]]}
554
+ self.Zakharov_ = {'name':'Zakharov', 'f':Zakharov, 'bounds':[[-5, 10], [-5, 10]], 'opt':[[0.0, 0.0], 0.0]}
555
+ self.THREE_HUMP_CAMEL_ = {'name':'THREE_HUMP_CAMEL', 'f':THREE_HUMP_CAMEL, 'bounds':[[-5, 5], [-5, 5]], 'opt':[[0.0, 0.0], 0.0]}
556
+ self.SIX_HUMP_CAMEL_ = {'name':'SIX_HUMP_CAMEL', 'f':SIX_HUMP_CAMEL, 'bounds':[[-3, 3], [-2, 2]], 'opt':[[0.0898, -0.7126], -1.0316]}
557
+ self.DIXON_PRICE_ = {'name':'DIXON_PRICE', 'f':DIXON_PRICE, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[1, 1/np.sqrt(2)], 0]}
558
+ self.ROSENBROCK_ = {'name':'ROSENBROCK', 'f':ROSENBROCK, 'bounds':[[-5, 10], [-5, 10]], 'opt':[[1, 1], 0]}
559
+ self.DE_JONG_ = {'name':'DE_JONG', 'f':DE_JONG, 'bounds':[[-65.536, 65.536], [-65.536, 65.536]]}
560
+ self.MICHALEWICZ_ = {'name':'MICHALEWICZ', 'f':MICHALEWICZ, 'bounds':[[0, np.pi], [0, np.pi]], 'opt':[[2.2, 1.57], -1.8013]}
561
+ self.BEALE_ = {'name':'BEALE', 'f':BEALE, 'bounds':[[-4.5, 4.5], [-4.5, 4.5]], 'opt':[[3, 0.5], 0]}
562
+ self.BRANIN_ = {'name':'BRANIN', 'f':BRANIN, 'bounds':[[-5, 10], [0, 15]], 'opt':[[-np.pi, 12.275], 0.397887]}
563
+ self.GOLDSTEIN_PRICE_ = {'name':'GOLDSTEIN_PRICE', 'f':GOLDSTEIN_PRICE, 'bounds':[[-2, 2], [-2, 2]], 'opt':[[0, -1], 3]}
564
+ self.PERM_D_BETA_ = {'name':'PERM_D_BETA', 'f':PERM_D_BETA, 'bounds':[[-2, 2], [-2, 2]], 'opt':[[1, 2], 0]}
565
+ self.dictionary = {'Ackley': self.Ackley_,
566
+ 'sixth_Bukin':self.sixth_Bukin_,
567
+ 'Cross_in_Tray':self.Cross_in_Tray_,
568
+ 'Drop_Wave':self.Drop_Wave_,
569
+ 'Eggholder':self.Eggholder_,
570
+ 'Griewank':self.Griewank_,
571
+ 'Holder_Table':self.Holder_Table_,
572
+ 'Levy':self.Levy_,
573
+ 'Rastrigin':self.Rastrigin_,
574
+ 'second_Schaffe':self.second_Schaffe_,
575
+ 'fourth_Schaffer':self.fourth_Schaffer_,
576
+ 'Schwefel':self.Schwefel_,
577
+ 'Shubert':self.Shubert_,
578
+ 'Styblinski_Tang':self.Styblinski_Tang_,
579
+ 'Easom':self.Easom_,
580
+ 'Bohachevsky':self.Bohachevsky_,
581
+ 'Perm_d_beta':self.Perm_d_beta_,
582
+ 'Rotated_Hyper_Ellipsoid':self.Rotated_Hyper_Ellipsoid_,
583
+ 'Sum_of_Different_Powers': self.Sum_of_Different_Powers_,
584
+ 'SUM_SQUARES':self.SUM_SQUARES_,
585
+ 'TRID':self.TRID_,
586
+ 'BOOTH': self.BOOTH_,
587
+ 'Matyas':self.Matyas_,
588
+ 'MCCORMICK': self.MCCORMICK_,
589
+ 'Power_Sum':self.Power_Sum_,
590
+ 'Zakharov':self.Zakharov_,
591
+ 'THREE_HUMP_CAMEL' :self.THREE_HUMP_CAMEL_,
592
+ 'SIX_HUMP_CAMEL': self.SIX_HUMP_CAMEL_,
593
+ 'DIXON_PRICE': self.DIXON_PRICE_,
594
+ 'ROSENBROCK_': self.ROSENBROCK_,
595
+ 'DE_JONG':self.DE_JONG_,
596
+ 'MICHALEWICZ': self.MICHALEWICZ_,
597
+ 'BEALE':self.BEALE_,
598
+ 'BRANIN': self.BRANIN_,
599
+ 'GOLDSTEIN_PRICE':self.GOLDSTEIN_PRICE_,
600
+ 'PERM_D_BETA':self.PERM_D_BETA_}
601
+ self.whole_list = list(self.dictionary.keys())
602
+
603
+
604
+ def plotly_graph(problem, df=None):
605
+ function=problem['f']
606
+ x_lb=problem['bounds'][0][0]
607
+ x_ub=problem['bounds'][0][1]
608
+ y_lb=problem['bounds'][1][0]
609
+ y_ub=problem['bounds'][1][1]
610
+
611
+ x = np.linspace(x_lb, x_ub, 100)
612
+ y = np.linspace(y_lb, y_ub, 100)
613
+ z = np.empty((100, 100))
614
+ for ind_y, j in enumerate(y):
615
+ for ind_x, i in enumerate(x):
616
+ z[ind_y][ind_x] = function(np.array([i, j]))
617
+
618
+ steps_ = int(np.max(df['iter']))
619
+
620
+ fig1 = go.Figure(data=[go.Surface(x=x, y=y, z=z)])
621
+ for step in range(steps_):
622
+ points = df[df['iter']==step]
623
+ points_x = list(points['x1'])
624
+ points_y = list(points['x2'])
625
+ points_z = list(points['f'])
626
+ fig1.add_scatter3d(x=np.array(points_x), y=np.array(points_y), z=np.array(points_z), mode='markers', visible=False, marker=dict(size=5, color="white", line=dict(width=1, color="black")))
627
+ fig1.update_layout(title=f"f = {step}")
628
+
629
+ # Create figure
630
+ fig = go.Figure(data=[go.Scatter3d(x=[], y=[], z=[],
631
+ mode="markers",
632
+ marker=dict(size=5, color="white", line=dict(width=1, color="black"))
633
+ ), fig1.data[0]]
634
+ )
635
+
636
+ # Frames
637
+ frames = [go.Frame(data=[go.Scatter3d(x=k['x'],
638
+ y=k['y'],
639
+ z=k['z']
640
+ ), fig1.data[0]
641
+ ],
642
+ traces= [0],
643
+ name=f'frame{ind}'
644
+ ) for ind, k in enumerate(fig1.data[1:])
645
+ ]
646
+
647
+ fig.update(frames=frames)
648
+
649
+ def frame_args(duration):
650
+ return {
651
+ "frame": {"duration": duration},
652
+ "mode": "immediate",
653
+ "fromcurrent": True,
654
+ "transition": {"duration": duration, "easing": "linear"},
655
+ }
656
+
657
+
658
+ sliders = [
659
+ {"pad": {"b": 10, "t": 60},
660
+ "len": 0.9,
661
+ "x": 0.1,
662
+ "y": 0,
663
+
664
+ "steps": [
665
+ {"args": [[f.name], frame_args(0)],
666
+ "label": str(k),
667
+ "method": "animate",
668
+ } for k, f in enumerate(fig.frames)
669
+ ]
670
+ }
671
+ ]
672
+
673
+ fig.update_layout(
674
+
675
+ updatemenus = [{"buttons":[
676
+ {
677
+ "args": [None, frame_args(150)],
678
+ "label": "Play",
679
+ "method": "animate",
680
+ },
681
+ {
682
+ "args": [[None], frame_args(150)],
683
+ "label": "Pause",
684
+ "method": "animate",
685
+ }],
686
+
687
+ "direction": "left",
688
+ "pad": {"r": 10, "t": 70},
689
+ "type": "buttons",
690
+ "x": 0.1,
691
+ "y": 0,
692
+ }
693
+ ],
694
+ sliders=sliders
695
+ )
696
+
697
+ fig.update_layout(sliders=sliders)
698
+ fig.write_html('animation.html')
699
+ return fig
700
+
701
+
702
+ problem=test_functions().PERM_D_BETA_
703
+ df, best_var, best_sol = custom_pso(problem, 20, 2, maxiter=100, solver="Newton", k=0.000000001, G=0.00000001, t=1.0, max_q=0.01, auto_reduce_search_space=True, dinamic=True)
704
+ #plotly_graph(problem, df)
705
+
706
+ print(best_sol)