raaraya commited on
Commit
3466da7
verified
1 Parent(s): 7aab566

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +1235 -0
app.py ADDED
@@ -0,0 +1,1235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from custom_pso import *
2
+ import streamlit as st
3
+
4
+
5
+ st.markdown('''
6
+ --------------------------------------------------------------
7
+ # Custom PSO algorithm for continuous domains
8
+
9
+ Autor: Rodrigo Araya
10
+
11
12
+ ''')
13
+ st.markdown("------")
14
+
15
+ st.markdown("### 1. Explicaci贸n Algoritmo")
16
+ with st.expander("Precedimiento General"):
17
+ st.markdown('''
18
+ 1. **Generar un archivo de tama帽o** `nSize=nPart`.
19
+ 2. **Generar** `nPart` **soluciones iniciales**. Cada soluci贸n contiene `nVar` variables.
20
+ 3. **Evaluar las soluciones** `nPart` **y agregarlas al archivo** ordenadas de la mejor a la peor soluci贸n.
21
+ 4. **Mientras no se cumpla la condici贸n de terminaci贸n** (iteraciones > maxiter):
22
+ 4.1. **Generar nuevas soluciones** utilizando alg煤n solucionador. Las opciones son: ['Random', 'Newton', 'Coulomb'].
23
+ 4.2. **Agregar las nuevas soluciones al archivo**.
24
+ 4.3. **Eliminar soluciones duplicadas**.
25
+ 4.4. **Evaluar las soluciones y ordenarlas** de la mejor a la peor soluci贸n.
26
+ 4.5. **Mantener en el archivo las mejores** `nPar` **soluciones**.
27
+ 4.6. **Guardar los resultados de la iteraci贸n en un historial** (dataframe).
28
+ 4.7. **Evaluar la condici贸n de terminaci贸n**. Si es negativa, regresar al paso 4.1.
29
+ ''')
30
+
31
+ st.image('Custom_PSO_workflow.png')
32
+
33
+ st.markdown("### 2. Metodos")
34
+ with st.expander("Random Method"):
35
+ c1, c2 = st.columns(2)
36
+ c1.markdown('''
37
+ Este solucionador genera una soluci贸n aleatoria para cada part铆cula.
38
+ Adem谩s, tambi茅n es posible aplicar un m茅todo de explotaci贸n donde el
39
+ espacio de b煤squeda se reduce en cada iteraci贸n.
40
+
41
+ 1. Verificar `auto_reduce_search_space`.
42
+ 2.1. Si `auto_reduce_search_space` es True.
43
+ 2.1.1. Calcular l铆mites `Lo` (lower bond) y `Up` (upper bond) basados en `x_`(soluciones inciales)
44
+ 2.2. Si `auto_reduce_search_space` es `False`.
45
+ 2.2.1. Establecer l铆mites `Lo` y `Up` en cero y uno respectivamente
46
+ 3. Generar nuevas posiciones aleatorias dentro de los l铆mites
47
+ 4. Ajustar las posiciones si exceden los l铆mites
48
+ 5. Evaluar las nuevas posiciones utilizando
49
+ 6. Devolver las nuevas posiciones y evaluaciones
50
+ ''')
51
+
52
+ c2.image('Random.jpg')
53
+
54
+ with st.expander("Newton Method"):
55
+ c1, c2 = st.columns(2)
56
+
57
+ c1.markdown(r'''
58
+ El procedimiento que genera las nuevas posiciones de las part铆culas
59
+ se basa en c贸mo los cuerpos se atraen entre s铆 a trav茅s de una fuerza
60
+ de atracci贸n (ley universal de la gravitaci贸n). Por lo tanto, el
61
+ procedimiento ser谩 el siguiente:
62
+
63
+ 1. Se reciben $n$ part铆culas en posiciones aleatorias dentro del
64
+ espacio de b煤squeda.
65
+ 2. A cada part铆cula se le asigna una masa basada en la soluci贸n
66
+ proporcionada por la part铆cula.
67
+ - Cuanto mejor sea la soluci贸n proporcionada por la part铆cula,
68
+ mayor ser谩 la masa asignada.
69
+ - M茅todos de asignaci贸n: Lineal, Exponencial, Cuadr谩tica (se puede
70
+ explorar una discusi贸n adicional sobre mejores m茅todos de asignaci贸n).
71
+ - En t茅rminos simples, se establece un rango de soluciones y se
72
+ ajusta a un rango de masa entre 0 y `max_mass` seg煤n alg煤n m茅todo de
73
+ asignaci贸n.
74
+ 3. Sea $F_{ij}$ la fuerza producida entre la part铆cula $i$ y $j$ ->
75
+ $F_{ij} = \frac{G \cdot m_i \cdot m_j}{r^2}$ donde $r$ es la distancia
76
+ entre las part铆culas, $m_i$ y $m_j$ son las masas de $i$ y $j$.
77
+ - Por lo tanto, la fuerza resultante sobre la part铆cula $i$ es
78
+ $FR_{i} = \sum_{j=1}^{n} \frac{G \cdot m_i \cdot m_j}{r_{ij}^2}$.
79
+ - Adem谩s, por la Ley de Newton, $F = m \cdot a$ -> $a = \frac{F}{m}$. Por lo tanto, las velocidades de cada part铆cula se calculan como $v_f = v_i + a \cdot t$ -> $v_f = v_i + \frac{F}{m} \cdot t$.
80
+ - Finalmente, las posiciones se actualizan de la siguiente manera: $x' = x + v_i \cdot t + \frac{a \cdot t^2}{2}$ -> $x' = x + v_i \cdot t + \frac{F}{m} \cdot \frac{t^2}{2}$.
81
+ - Aplicando la suposici贸n $v_i=0$ -> $x' = x + \frac{F}{m} \cdot \frac{t^2}{2}$.
82
+
83
+ ''')
84
+
85
+ c2.image('Newton.jpg')
86
+
87
+ st.markdown(r'''
88
+ Finalmente, si las part铆culas est谩n demasiado cerca, podr铆a causar un
89
+ problema. Si $r_{ij}^{2}$ tiende a cero, entonces la fuerza de atracci贸n ser铆a
90
+ demasiado fuerte, llevando a las part铆culas a separarse abruptamente
91
+ entre s铆. Para evitar este comportamiento, se establece una peque帽a
92
+ distancia `0.00000001`. Si la distancia entre dos part铆culas es menor
93
+ que esta distancia, se considerar谩 que ambas part铆culas han colisionado.
94
+
95
+ Por 煤ltimo, si m谩s de la mitad de las part铆culas han colisionado, se
96
+ aplicar谩 una funci贸n aleatoria para generar nuevas soluciones sobre
97
+ un espacio de b煤squeda reducido.
98
+ ''')
99
+
100
+ with st.expander("Coulomb Method"):
101
+ c1, c2 = st.columns(2)
102
+ c1.markdown(r'''
103
+ 1. Se reciben n part铆culas en posiciones diferentes dentro del espacio
104
+ de b煤squeda.
105
+ 2. A cada part铆cula se le asigna un tipo de carga positiva.
106
+ 3. La magnitud de la carga de cada part铆cula est谩 directamente relacionada
107
+ con la funci贸n objetivo.
108
+ - M茅todos de Asignaci贸n: Lineal, Exponencial, Cuadr谩tica (se pueden
109
+ explorar m茅todos de asignaci贸n mejores)
110
+ - En resumen, se establece un rango de soluci贸n y se ajusta a un
111
+ rango de carga entre 0 y `max_q` seg煤n alg煤n m茅todo de asignaci贸n.
112
+ 4. Cada part铆cula tiene una velocidad. Estas comienzan en reposo pero
113
+ cambiar谩n a lo largo de las iteraciones del algoritmo.
114
+ 5. Las `P` part铆culas con los mejores valores obtenidos de la iteraci贸n
115
+ permanecer谩n en reposo y emitir谩n un campo el茅ctrico de signo opuesto
116
+ (negativo) atrayendo al resto.
117
+ - Sea $E = \frac{kQ}{r^2}$ el campo magn茅tico en un punto ubicado a
118
+ una distancia $r$ de la fuente.
119
+ - Sea $Fe$ la fuerza el茅ctrica entre el campo magn茅tico y la part铆cula
120
+ -> $Fe = E*q_{0}$ donde $q_{0}$ es la carga de la part铆cula.
121
+ - Sea $F_{ij}$ la fuerza producida entre la part铆cula $i$ y $j$ ->
122
+ $F_{ij} = \frac{k*q_{i}*q_{j}}{r^2}$ donde $r$ es la distancia
123
+ entre las part铆culas, $q_{i}$ y $q_{j}$ son las cargas de $i$ y $j$.
124
+ - Por lo tanto, las fuerzas resultantes de la part铆cula $i$ son
125
+ $FR_{i} = \sum_{p=1}^{P} \frac{k*Q_{p}*Q_{i}}{r_{ip}^{2}} + \sum_{j=P+1}^{n} \frac{k*Q_{i}*Q_{j}}{r_{ij}^{2}}$
126
+ - Adem谩s, por la Ley de Newton, $F=m*a$ -> $Fe=m*a$. Por lo tanto, las
127
+ velocidades de cada part铆cula se calculan $v_{f}=v_{i}+a*t$ -> $v_{f} = v_{i} + (\frac{FR_{i}}{m})*t$
128
+ - Las posiciones se actualizan de la siguiente manera
129
+ $x = v_{i}*t + \frac{(a*t^{2})}{2}$ -> $x = v_{i}*t + (\frac{FR_{i}}{m})*t^{2}*0.5$
130
+ ''')
131
+ c2.image('Coulomb.jpg')
132
+
133
+ st.markdown(r'''
134
+ Finalmente, si las part铆culas est谩n demasiado cerca, podr铆a causar un
135
+ problema. Si $r_{ij}^{2}$ tiende a cero, entonces la fuerza el茅ctrica ser铆a
136
+ demasiado fuerte, llevando a las part铆culas a separarse abruptamente
137
+ entre s铆. Para evitar este comportamiento, se establece una peque帽a
138
+ distancia `0.00000001`. Si la distancia entre dos part铆culas es menor
139
+ que esta distancia, se considerar谩 que ambas part铆culas han colisionado.
140
+
141
+ Por 煤ltimo, si m谩s de la mitad de las part铆culas han colisionado, se
142
+ aplicar谩 una funci贸n aleatoria para generar nuevas soluciones sobre
143
+ un espacio de b煤squeda reducido.
144
+
145
+ ''')
146
+
147
+ st.markdown("### 3. Codigos")
148
+
149
+ with st.expander("F_newton.py"):
150
+ st.markdown(r'''
151
+ ```python
152
+ import numpy as np
153
+
154
+ def normaliazed_direction(ori, des):
155
+ dir_ = des-ori
156
+ nor_dir_ = dir_/np.sqrt(np.sum(dir_**2))
157
+ nor_dir_=np.nan_to_num(nor_dir_)
158
+ return nor_dir_
159
+
160
+ def distance(ori, des):
161
+ dis_ = des-ori
162
+ return dis_
163
+
164
+ def acc(F, m):
165
+ Npar=F.shape[0]
166
+ Nvar=F.shape[1]
167
+ # Matriz de aceleracion
168
+ Ac = np.full((Npar, Nvar), 0.0)
169
+ for ind_r, row in enumerate(Ac):
170
+ for ind_c, col in enumerate(row):
171
+ Ac[ind_r][ind_c]=F[ind_r][ind_c]/m[ind_r]
172
+ return Ac
173
+
174
+ def F_newton(x_, m, minimize=True, G=0.0001, keep_best=False, weight_method='Linear', t=1.0, max_mass=100.0):
175
+ """ The procedure that generates the new positions of the particles is based on how bodies are attracted to each other
176
+ through an attractive force (universal law of gravitation). Thus, the procedure will be as follows:
177
+
178
+ 1. n particles are created at random positions within the search space.
179
+ 2. Each particle is assigned a mass based on the solution provided by the particle.
180
+ 2.1 The better the solution provided by the particle, the greater the assigned mass.
181
+ 2.2 Assignment methods -> Linear, Exponential, Quadratic (further discussion on better assignment methods can be explored).
182
+ 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.
183
+ 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.
184
+ 3.1 Thus, the resulting force on particle i is FRi = 危{j=1} G*mi*mj/(rij^2).
185
+ 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.
186
+ 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.
187
+ 3.4 Applying the assumption vi=0 -> x' = x + (F/m)*(t^2)/2.
188
+
189
+ Args:
190
+ x_ (array(size=(Npar, Nvar)): current particle positions
191
+ m (array(size=(Npar, 1))): current mass of each particle
192
+ minimize (bool, optional): solver objective. Defaults to True.
193
+ G (float, optional): gravitational constant. Defaults to 0.0001.
194
+ keep_best (bool, optional): It will save the best value obtained in each iteration. Defaults to False.
195
+ weight_method (str, optional): method to reassign mass. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
196
+ t (float, optional): time. Defaults to 1.0.
197
+ max_mass (float, optional): upper bound of mass to assing. Defaults to 100.0.
198
+
199
+ Returns:
200
+ F_total, Ac, new_pos: returns the force obtained on each particle, their acceleration,
201
+ and their new positions
202
+ """
203
+ Npar=x_.shape[0]
204
+ Nvar=x_.shape[1]
205
+
206
+ # Distance Matrix
207
+ dis = []
208
+ for ind_r, row in enumerate(x_):
209
+ for ind_c, col in enumerate(x_):
210
+ dis.append(distance(x_[ind_r], x_[ind_c]))
211
+ dis=np.array(dis).reshape((Npar,Npar,Nvar))
212
+
213
+ # Direction Matrix
214
+ d = []
215
+ for ind_r, row in enumerate(x_):
216
+ for ind_c, col in enumerate(x_):
217
+ d.append(normaliazed_direction(x_[ind_r], x_[ind_c]))
218
+ d=np.array(d).reshape((Npar,Npar,Nvar))
219
+
220
+ colisioned_part = []
221
+ r_2 = np.zeros((Npar, Npar))
222
+ for ind_r, row in enumerate(dis):
223
+ for ind_c, col in enumerate(dis):
224
+ value = dis[ind_r][ind_c]
225
+ value_2 = value**2
226
+ value_sum = np.sum(value_2)
227
+ if value_sum < 0.00000001: # Particles have practically collided. Notice later that F_=0.0 ahead.
228
+ r_2[ind_r][ind_c] = 0.0
229
+ if ind_r != ind_c:
230
+ colisioned_part.append(ind_c)
231
+ else:
232
+ r_2[ind_r][ind_c] = value_sum
233
+ colisioned_part_ = np.unique(np.array(colisioned_part))
234
+
235
+
236
+ # Each particle is assigned a mass magnitude based on the solution provided by the particle.
237
+ m=np.array(m)
238
+ min_value = m[0]
239
+ max_value = m[-1]
240
+
241
+ if minimize:
242
+ m = -1.0*(m-max_value-1.0)
243
+ max_value = m[0]
244
+
245
+ if weight_method=="Linear":
246
+ # We adjust the mass according to the following range [0, max_mass].
247
+ m = (m/max_value)*max_mass
248
+ reverse_ind = [len(m)-i-1 for i in range(len(m))]
249
+ m = m[reverse_ind]
250
+
251
+ elif weight_method=="Quadratic":
252
+ # We adjust the mass according to the following range [0, max_mass].
253
+ m_2=m**2
254
+ m=(m_2/np.max(m_2))*max_mass
255
+ reverse_ind = [len(m)-i-1 for i in range(len(m))]
256
+ m = m[reverse_ind]
257
+
258
+ elif weight_method=="Exponential":
259
+ # We adjust the mass according to the following range [0, max_mass].
260
+ m_exp=np.array([np.exp(i) for i in m])
261
+ m=(m_exp/np.max(m_exp))*max_mass
262
+ reverse_ind = [len(m)-i-1 for i in range(len(m))]
263
+ m = m[reverse_ind]
264
+
265
+ m = np.nan_to_num(m, nan=0.0001)
266
+ Npar=d.shape[0]
267
+ Nvar=d.shape[2]
268
+ F=np.full((Npar, Npar, Nvar), 0.0)
269
+ for ind_r, row in enumerate(dis):
270
+ for ind_c, col in enumerate(row):
271
+ # The magnitude of the attraction force F_ is calculated.
272
+ d_2=r_2[ind_r][ind_c]
273
+ if d_2==0:
274
+ F_=0.0
275
+ else:
276
+ m1=m[ind_r]
277
+ m2=m[ind_c]
278
+ F_=float(G*m1*m2/d_2)
279
+ F[ind_r][ind_c]=F_*d[ind_r][ind_c]
280
+ F_total = np.sum(F, axis=1)
281
+ # Note: Between two particles, the attractive forces will be the same, but not their accelerations.
282
+ # 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)
283
+ Ac=acc(F_total, m)
284
+ Ac = np.nan_to_num(Ac, nan=0.0)
285
+ # 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).
286
+ x__ = 0.5*Ac*(t**2)
287
+ new_pos=x_ + x__
288
+
289
+ if keep_best==True:
290
+ best_ind = np.argmin(m)
291
+ Ac[best_ind]=0.0
292
+ new_pos=x_ + 0.5*Ac*(t**2)
293
+
294
+ # If more than half of the particles have collided, those that have collided will move randomly within the reduced search space.
295
+ max_crash_part=Npar/2
296
+ if len(colisioned_part_)>max_crash_part:
297
+ lo = np.min(new_pos, axis=0)
298
+ up = np.max(new_pos, axis=0)
299
+ for i in colisioned_part_:
300
+ new_pos[i] = np.random.uniform(low=lo, high=up, size=(1, Nvar))
301
+
302
+ return F_total, Ac, new_pos
303
+ ```''')
304
+
305
+ with st.expander('F_coulomb.py'):
306
+ st.markdown('''
307
+ ```python
308
+ import numpy as np
309
+
310
+ def normaliazed_direction(ori, des):
311
+ dir_ = des-ori
312
+ nor_dir_ = dir_/np.sqrt(np.sum(dir_**2))
313
+ nor_dir_=np.nan_to_num(nor_dir_)
314
+ return nor_dir_
315
+
316
+ def distance(ori, des):
317
+ dis_ = des-ori
318
+ return dis_
319
+
320
+ def acc(F, m):
321
+ Npar=F.shape[0]
322
+ Nvar=F.shape[1]
323
+ # Acceleration Matrix
324
+ Ac = np.full((Npar, Nvar), 0.0)
325
+ for ind_r, row in enumerate(Ac):
326
+ for ind_c, col in enumerate(row):
327
+ Ac[ind_r][ind_c]=F[ind_r][ind_c]/m[ind_r]
328
+ return Ac
329
+
330
+ def F_coulomb(x, v, q, P, minimize=True, k=0.0001, weight_method='Linear', t=1.0, max_q=100.0):
331
+ """ The procedure that generates the new positions of the particles is based on how particles with positive and negative charges move
332
+ in electric fields. Thus, the procedure will be as follows:
333
+
334
+ 1. n particles are created at random positions within the search space.
335
+ 2. Each particle is assigned a positive charge type.
336
+ 3. The magnitude of each particle's charge is directly related to the objective function.
337
+ 3.1 Assignment Methods -> Linear, Exponential, Quadratic (better assignment methods can be explored)
338
+ 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.
339
+ 4. Each particle holds a velocity. These start at rest but will change over the iterations of the algorithm.
340
+ 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.
341
+ 5.1 Let E = k*Q/r^2 be the magnetic field at a point located at a distance r from the source.
342
+ 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.
343
+ 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.
344
+ 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)
345
+ 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
346
+ 5.6 The positions are updated as follows x = vi*t + (a*t^2)/2 -> x = vi*t + (FRi/m)*(t^2)/2
347
+
348
+ 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
349
+ the particles to abruptly separate from each other. To avoid this behavior, a small distance (0.00000001) is established. If the distance
350
+ between two particles is smaller than that distance, it will be considered that both particles have collided. Then, if more than half of
351
+ the particles have collided, we will apply a random function to generate new solutions over a reduced search space.
352
+
353
+ Args:
354
+ x (array(size=(Npar, Nvar)): current particle positions
355
+ v (array(size=(Npar, Nvar)): current particle velocity
356
+ q (array(size=(Npar, 1))): current electric charge of each particle
357
+ P (int): quantity of electric fields (the best P particles become electric fields)
358
+ minimize (bool, optional): solver objective. Defaults to True.
359
+ k (float, optional): electric constant. Defaults to 0.0001.
360
+ weight_method (str, optional): method to reassign electric charges. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
361
+ t (float, optional): time. Defaults to 1.0.
362
+ max_q (float, optional): upper bound of electric charge to assing. Defaults to 100.0.
363
+
364
+ Returns:
365
+ F_total, Ac, vf, new_pos: returns the force obtained on each particle, their acceleration, their velocities
366
+ and their new positions
367
+ """
368
+
369
+ Npar=x.shape[0]
370
+ Nvar=x.shape[1]
371
+
372
+ # Distance Matrix
373
+ dis = []
374
+ for ind_r, row in enumerate(x):
375
+ for ind_c, col in enumerate(x):
376
+ dis.append(distance(x[ind_r], x[ind_c]))
377
+ dis=np.array(dis).reshape((Npar,Npar,Nvar))
378
+
379
+ # Direction Matrix
380
+ d = []
381
+ for ind_r, row in enumerate(x):
382
+ for ind_c, col in enumerate(x):
383
+ d.append(normaliazed_direction(x[ind_r], x[ind_c]))
384
+ d=np.array(d).reshape((Npar,Npar,Nvar))
385
+
386
+ colisioned_part = []
387
+ r_2 = np.zeros((Npar, Npar))
388
+ for ind_r, row in enumerate(dis):
389
+ for ind_c, col in enumerate(dis):
390
+ value = dis[ind_r][ind_c]
391
+ value_2 = value**2
392
+ value_sum = np.sum(value_2)
393
+ if value_sum < 0.00000001: # Particles have practically collided. Notice later that fe=0.0 ahead.
394
+ r_2[ind_r][ind_c] = 0.0
395
+ if ind_r != ind_c:
396
+ colisioned_part.append(ind_c)
397
+ else:
398
+ r_2[ind_r][ind_c] = value_sum
399
+ colisioned_part_ = np.unique(np.array(colisioned_part))
400
+
401
+
402
+ # Each particle is assigned an electric charge magnitude based on the solution provided by the particle.
403
+ q=np.array(q)
404
+ min_value = q[0]
405
+ max_value = q[-1]
406
+ if minimize:
407
+ q = -1.0*(q-max_value-1.0)
408
+ max_value = q[0]
409
+
410
+ if weight_method=="Linear":
411
+ # We adjust the charges according to the following range [0, max_q].
412
+ q = (q/max_value)*max_q
413
+ 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.
414
+ q = q[reverse_ind]
415
+
416
+ elif weight_method=="Quadratic":
417
+ # We adjust the charges according to the following range [0, max_q].
418
+ q_2=q**2
419
+ q=(q_2/np.max(q_2))*max_q
420
+ 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.
421
+ q = q[reverse_ind]
422
+
423
+ elif weight_method=="Exponential":
424
+ # We adjust the charges according to the following range [0, max_q].
425
+ q_exp=np.array([np.exp(i) for i in q])
426
+ q=(q_exp/np.max(q_exp))*max_q
427
+ 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.
428
+ q = q[reverse_ind]
429
+
430
+ Npar=d.shape[0]
431
+ Nvar=d.shape[2]
432
+ F=np.full((Npar, Npar, Nvar), 0.0)
433
+ for ind_r, row in enumerate(dis):
434
+ for ind_c, col in enumerate(row):
435
+ # The magnitude of the electric force Fe is calculated.
436
+ d_2=r_2[ind_r][ind_c]
437
+ if d_2==0:
438
+ Fe=0.0
439
+ else:
440
+ q1=q[ind_r]
441
+ q2=q[ind_c]
442
+ Fe=float(k*q1*q2/d_2)
443
+ if ind_r >= P and ind_c >= P: # Repulsive forces are generated between particles of the same sign.
444
+ F[ind_r][ind_c]=-1.0*Fe*d[ind_r][ind_c]
445
+ else: # There is attraction between particles and electric fields.
446
+ F[ind_r][ind_c]=Fe*d[ind_r][ind_c]
447
+ F[:P, :P] = 0.0
448
+ F_total = np.sum(F, axis=1)
449
+ F_total[:P]=0.0
450
+ # Remember that F=ma -> m1*a1 = m2*a2. So, if m1>m2 -> a2>a1 -> Particles with greater mass have less acceleration.
451
+ # For this method, the weight of the particle is not important, so we will set them all to be equal to 1.0.
452
+ m=np.ones(Npar)
453
+ # vf = acc + vi*t
454
+ Ac=acc(F_total, m)
455
+ vf = Ac + t*v
456
+ Ac[:P]=0.0
457
+ vf[:P]=0.0 # The velocity of the magnetic fields is set to 0.0.
458
+ # Finally, Xf = xi + vi*t + 0.5*acc*(t^2) (Final Position)
459
+ x__=v*t + 0.5*Ac*(t**2)
460
+ new_pos = x + x__
461
+
462
+ # If more than half of the particles have collided, those that have collided will move randomly within the reduced search space.
463
+ max_crash_part=Npar/2
464
+ if len(colisioned_part_)>max_crash_part:
465
+ lo = np.min(new_pos, axis=0)
466
+ up = np.max(new_pos, axis=0)
467
+ for i in colisioned_part_:
468
+ new_pos[i] = np.random.uniform(low=lo, high=up, size=(1, Nvar))
469
+
470
+ return F_total, Ac, vf, new_pos
471
+ ```
472
+ ''')
473
+
474
+ with st.expander("Custom_pso.py"):
475
+ st.markdown('''
476
+ ```python
477
+
478
+ import math
479
+ import numpy as np
480
+ import pandas as pd
481
+ from F_newton import F_newton
482
+ from F_coulomb import F_coulomb
483
+ import plotly.graph_objects as go
484
+ import plotly.express as px
485
+
486
+
487
+ def Random_(problem, x_, auto_reduce_search_space=False):
488
+ """ This solver generates a random solution for each particle. Additionally, it is also possible to
489
+ apply an exploitation method where the search space is reduced on each iteration.
490
+
491
+ Args:
492
+ problem (dic): Dictionary that include: objective function, lower bound and upper bound of each variable and optimal solution
493
+ x_ (array(size=())): Current position of each particle
494
+ 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.
495
+
496
+ Returns:
497
+ Stemp, S_f: New positions for each particle and their performance after being evaluated with the objective function.
498
+ """
499
+
500
+
501
+ nPart = x_.shape[0]
502
+ nVar = x_.shape[1]
503
+
504
+ if auto_reduce_search_space:
505
+ Lo = np.min(x_, axis=0)
506
+ Up = np.max(x_, axis=0)
507
+ else:
508
+ Lo = np.zeros(nVar)
509
+ Up = np.ones(nVar)
510
+
511
+ new_x = np.random.uniform(low=Lo, high=Up, size=(nPart, nVar))
512
+
513
+ Stemp = np.zeros((nPart,nVar))
514
+ for k in range(nPart):
515
+ for i in range(nVar):
516
+ Stemp[k][i] = new_x[k][i]
517
+ if Stemp[k][i] > Up[i]:
518
+ Stemp[k][i] = Up[i]
519
+ elif Stemp[k][i] < Lo[i]:
520
+ Stemp[k][i] = Lo[i]
521
+ f,S_r,maximize = mp_evaluator(problem, Stemp)
522
+
523
+ S_f = np.zeros((nPart,1))
524
+
525
+ for i in range(len(S_r)):
526
+ S_f[i] = f[i]
527
+ return Stemp, S_f
528
+
529
+ def F_newton_(problem, x_, m, Lo, Up, G=0.00001, keep_best=True, weight_method='Linear', t=1.0, max_mass=100.0):
530
+ _, _, new_x = F_newton(x_, m, minimize=True, G=G, keep_best=keep_best, weight_method=weight_method, t=t, max_mass=max_mass)
531
+ nPart=x_.shape[0]
532
+ nVar=x_.shape[1]
533
+ Stemp = np.zeros((nPart,nVar))
534
+ for k in range(nPart):
535
+ for i in range(nVar):
536
+ Stemp[k][i] = new_x[k][i]
537
+ if Stemp[k][i] > Up[i]:
538
+ Stemp[k][i] = Up[i]
539
+ elif Stemp[k][i] < Lo[i]:
540
+ Stemp[k][i] = Lo[i]
541
+ f,S_r,maximize = mp_evaluator(problem, Stemp)
542
+
543
+ S_f = np.zeros((nPart,1))
544
+
545
+ for i in range(len(S_r)):
546
+ S_f[i] = f[i]
547
+ return Stemp, S_f
548
+
549
+ def F_coulomb_(problem, x_, v, q, P, Lo, Up, k=0.00001, weight_method='Linear', t=1.0, max_q=100.0):
550
+ _, _, v, new_x = F_coulomb(x_, v, q, P=P, minimize=True, k=k, weight_method=weight_method, t=t, max_q=max_q)
551
+ nPart=x_.shape[0]
552
+ nVar=x_.shape[1]
553
+ Stemp = np.zeros((nPart,nVar))
554
+ for k in range(nPart):
555
+ for i in range(nVar):
556
+ Stemp[k][i] = new_x[k][i]
557
+ if Stemp[k][i] > Up[i]:
558
+ Stemp[k][i] = Up[i]
559
+ elif Stemp[k][i] < Lo[i]:
560
+ Stemp[k][i] = Lo[i]
561
+ f,S_r,maximize = mp_evaluator(problem, Stemp)
562
+
563
+ S_f = np.zeros((nPart,1))
564
+
565
+ for i in range(len(S_r)):
566
+ S_f[i] = f[i]
567
+ return Stemp, v, S_f
568
+
569
+ def evaluator(problem, x):
570
+ # 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
571
+ # 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:
572
+ # y1=ax+b -> -5 = a*0+b -> b=-5
573
+ # y1=ax+b -> 5 = a*1+-5 -> a=10
574
+ # y1=10*x+-5
575
+ # y2=ax+b -> -2 = a*0+b -> b=-2
576
+ # y2=ax+b -> 2 = a*1+-2 -> a=4
577
+ # y2=4*x+-2
578
+ # Luego se aplica y1(x1) e y2(x2) respectivamente. Extendible a n dimensiones.
579
+ # Dado que [0, 1] no cambia, se generaliza la formula b=lb y a=ub-lb -> y_{i} = (ub-lb)_{i}*x + lb_{i}
580
+ lb = [var[0] for var in problem['bounds']]
581
+ ub = [var[1] for var in problem['bounds']]
582
+ x = [(ub[ind]-lb[ind])*i+lb[ind] for ind, i in enumerate(x)]
583
+ # calculate fitness
584
+ f = problem['f'](x)
585
+ fitness = dict(Obj=f)
586
+ return fitness
587
+
588
+ def mp_evaluator(problem, x):
589
+ results = [evaluator(problem, c) for c in x]
590
+ f = [r['Obj'] for r in results]
591
+ # maximization or minimization problem
592
+ maximize = False
593
+ return (f, [r for r in results],maximize)
594
+
595
+ def correct_x(problem, x_):
596
+ lb = [var[0] for var in problem['bounds']]
597
+ ub = [var[1] for var in problem['bounds']]
598
+ x = np.empty(x_.shape)
599
+ for ind, row in enumerate(x_):
600
+ corr_row = np.array([(ub[ind]-lb[ind])*i+lb[ind] for ind, i in enumerate(row)])
601
+ x[ind]=corr_row
602
+ return x
603
+
604
+
605
+
606
+ 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):
607
+ """The procedure of this algorithm is as follows:
608
+ 1. Generate a file of size nSize=nPart.
609
+ 2. Generate nPart initial solutions. Each solution contains nVar variables.
610
+ 3. Evaluate the nPart solutions and add them to the file ordered from best to worst solution.
611
+ 4. While the termination condition is not met (iterations > maxiter):
612
+ 4.1 Generate new solutions using some solver. Options = ['Random', 'Newton', 'Coulomb']
613
+ 4.2 Add new solutions to the file.
614
+ 4.3 Remove duplicate solutions.
615
+ 4.4 Evaluate solutions and sort from best to worst solution.
616
+ 4.5 Keep in the file the nPar best solutions.
617
+ 4.6 Save iteration results in a history (dataframe).
618
+ 4.7 Evaluate termination condition. If negative, return to step 4.1.
619
+
620
+ Args:
621
+ problem (dic): Dictionary that include: objective function, lower bound and upper bound of each variable and optimal solution
622
+ nPart (_type_): Quantity of particles
623
+ nVar (_type_): Quantity of variables
624
+ maxiter (_type_): Maximum number of iterations.
625
+ seed (int, optional): set the generation of random numbers. Defaults to 0.
626
+ solver (str, optional): solver to apply. Defaults to 'Newton'. Options=['Random', 'Newton', 'Coulomb'].
627
+
628
+ Random Solver:
629
+ 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.
630
+
631
+ Newton Solver:
632
+ G (float, optional): Gravitational constant. Defaults to 0.00001.
633
+ keep_best (bool, optional): It will keep the best value obtained in each iteration. Defaults to True.
634
+ weight_method (str, optional): method to reassign particle mass. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
635
+ t (float, optional): time. Defaults to 1.0.
636
+ max_mass (float, optional): upper bound of mass to assing. Defaults to 100.0.
637
+ 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.
638
+
639
+ Coulomb Solver:
640
+ P (int): quantity of electric fields (the best P particles become electric fields)
641
+ max_q (float, optional): upper bound of electric charge to assing. Defaults to 100.0.
642
+ weight_method (str, optional): method to reassign electric charges. Defaults to 'Linear'. Options=['Linear', 'Quadratic', 'Exponential'].
643
+ k (float, optional): electric constant. Defaults to 0.0001.
644
+ t (float, optional): time. Defaults to 1.0.
645
+ 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.
646
+
647
+
648
+ Returns:
649
+ df, best_var, best_sol: dataframe contening data from all iterations, the best variables and the best value obtained
650
+ """
651
+
652
+ # number of variables
653
+ parameters_v = [f'x{i+1}' for i in range(nVar)]
654
+
655
+ # number of variables
656
+ nVar = len(parameters_v)
657
+
658
+ # size of solution archive
659
+ nSize = nPart
660
+
661
+ # number of Particules
662
+ nPart = nPart
663
+
664
+ # maximum iterations
665
+ maxiter = maxiter
666
+
667
+ # bounds of variables
668
+ Up = [1]*nVar
669
+ Lo = [0]*nVar
670
+
671
+ # dinamic q
672
+ din_max_q=max_q
673
+
674
+ # dinamic mass
675
+ din_max_mass = max_mass
676
+
677
+ # initilize matrices
678
+ S = np.zeros((nSize,nVar))
679
+ S_f = np.zeros((nSize,1))
680
+
681
+ # initial velocity
682
+ v = np.zeros((nSize,nVar))
683
+
684
+ # history
685
+ columns_ = ['iter']
686
+ for i in parameters_v: columns_.append(i)
687
+ columns_.append('f')
688
+ df = pd.DataFrame(columns=columns_)
689
+
690
+ # generate first random solution
691
+ #np.random.seed(seed)
692
+ Srand = np.random.uniform(low=0,high=1,size=(nPart, nVar))
693
+ f,S_r,maximize = mp_evaluator(problem, Srand)
694
+
695
+
696
+ for i in range(len(S_r)):
697
+ S_f[i] = f[i]
698
+
699
+ # add responses and "fitness" column to solution
700
+ S = np.hstack((Srand, v, S_f))
701
+ # sort according to fitness (last column)
702
+ S = sorted(S, key=lambda row: row[-1],reverse = maximize)
703
+ S = np.array(S)
704
+
705
+ # save each iteration
706
+ iter_n = np.full((nPart, 1), 0.0)
707
+ x_ = S[:, 0:nVar]
708
+ x = correct_x(problem, x_)
709
+ res = np.reshape(S[:, -1], (nPart, 1))
710
+ rows=np.hstack((iter_n, x, res))
711
+ df_new = pd.DataFrame(rows, columns=columns_)
712
+ df = pd.concat([df, df_new])
713
+
714
+ iterations=1
715
+
716
+ # iterations
717
+ while True:
718
+ # get a new solution
719
+ if solver == "Random":
720
+ Stemp, S_f = Random_(problem, S[:, :nVar], auto_reduce_search_space=auto_reduce_search_space)
721
+
722
+ elif solver=="Newton":
723
+ din_mass=0
724
+ if dinamic:
725
+ din_mass = ((maxiter-iterations)/(maxiter))*(np.max(S_f)-np.min(S_f))
726
+ else:
727
+ din_mass=max_mass
728
+
729
+ m = np.reshape(S[:, -1], (nPart, 1))
730
+ 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)
731
+
732
+ elif solver == "Coulomb":
733
+ din_q=0
734
+ if dinamic:
735
+ din_q = ((maxiter-iterations)/(maxiter))*(np.max(S_f)-np.min(S_f))
736
+ else:
737
+ din_q=max_q
738
+ q = np.reshape(S[:, -1], (nPart, 1))
739
+ 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)
740
+
741
+ # add responses and "fitness" column to solution
742
+ Ssample = np.hstack((Stemp, v, S_f))
743
+
744
+ # add new solutions in the solutions table
745
+ Solution_temp = np.vstack((S,Ssample))
746
+
747
+ # delate duplicated rows
748
+ Solution_temp = np.unique(Solution_temp, axis=0)
749
+
750
+ # sort according to "fitness"
751
+ Solution_temp = sorted(Solution_temp, key=lambda row: row[-1],reverse = maximize)
752
+ Solution_temp = np.array(Solution_temp)
753
+
754
+ # keep best solutions
755
+ S = Solution_temp[:nSize][:]
756
+
757
+ # save each iteration
758
+ iter_n = np.full((nPart, 1), iterations)
759
+ x_ = S[:, 0:nVar]
760
+ x = correct_x(problem, x_)
761
+ res = np.reshape(S[:, -1], (nPart, 1))
762
+ rows=np.hstack((iter_n, x, res))
763
+ df_new = pd.DataFrame(rows, columns=columns_)
764
+ df = pd.concat([df, df_new])
765
+
766
+ iterations += 1
767
+ if iterations > maxiter:
768
+ break
769
+
770
+ best_sol = np.min(df['f'])
771
+ ind_best_sol = np.argmin(df['f'])
772
+ best_var = df.iloc[ind_best_sol, 1:len(parameters_v)+1]
773
+ return df, best_var, best_sol
774
+
775
+ # Test Functions.
776
+ # Adapted from "https://www.sfu.ca/~ssurjano/optimization.html"
777
+
778
+ def Ackley(x, a=20.0, b=0.2, c=2.0*np.pi):
779
+ d = len(x)
780
+ sum1 = np.sum(np.square(x))
781
+ sum2 = np.sum(np.array([np.cos(c*i) for i in x]))
782
+ term1 = -a * np.exp(-b * np.sqrt(sum1 / d))
783
+ term2 = -np.exp(sum2 / d)
784
+ return term1 + term2 + a + np.exp(1)
785
+
786
+ def sixth_Bukin(x):
787
+ return 100 * np.sqrt(np.abs(x[1] - 0.01*x[0]**2)) + 0.01*np.abs(x[0] + 10)
788
+
789
+ def Cross_in_Tray(x):
790
+ 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)
791
+
792
+ def Drop_Wave(x):
793
+ 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)
794
+
795
+ def Eggholder(x):
796
+ 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))))
797
+
798
+ def Griewank(x):
799
+ sum_part=0.0
800
+ prod_part=1.0
801
+ for i, xi in enumerate(x):
802
+ sum_part += xi/4000.0
803
+ prod_part *= np.cos(xi/np.sqrt(i+1))
804
+ return sum_part - prod_part + 1.0
805
+
806
+ def Holder_Table(x):
807
+ 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))))
808
+
809
+ def Levy(x):
810
+ # d dimensions
811
+ w1 = 1.0 + (x[0]-1.0)/4.0
812
+ wd = 1.0 + (x[-1]-1.0)/4.0
813
+ sum_part = 0.0
814
+ for i, xi in enumerate(x):
815
+ wi = 1.0 + (xi-1.0)/4.0
816
+ sum_part += ((wi-1.0)**2)*(1.0 + 10.0*math.pow(np.sin(np.pi*wi+1.0), 2))
817
+ 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))
818
+
819
+ def Rastrigin(x):
820
+ # d dimensions
821
+ d = len(x)
822
+ sum_part = 0.0
823
+ for i, xi in enumerate(x):
824
+ sum_part += (xi**2) - 10.0*np.cos(2*np.pi*xi)
825
+ return 10*d + sum_part
826
+
827
+ def second_Schaffe(x):
828
+ 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
829
+
830
+ def fourth_Schaffer(x):
831
+ 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
832
+
833
+ def Schwefel(x):
834
+ # d dimensions
835
+ d = len(x)
836
+ sum_part = 0.0
837
+ for xi in x:
838
+ sum_part += xi*np.sin(np.sqrt(np.abs(xi)))
839
+ return 418.9829*d - sum_part
840
+
841
+ def Shubert(x):
842
+ sum_1 = 0.0
843
+ sum_2 = 0.0
844
+ for i in np.arange(5):
845
+ i = float(i + 1)
846
+ sum_1 += i*np.cos((i+1)*x[0] + i)
847
+ sum_2 += i*np.cos((i+1)*x[1] + i)
848
+ return sum_1*sum_2
849
+
850
+ def Styblinski_Tang(x):
851
+ f = (sum([math.pow(i,4)-16*math.pow(i,2)+5*i for i in x])/2)
852
+ return f
853
+
854
+ def Easom(x):
855
+ 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)))
856
+ return f
857
+
858
+ def Bohachevsky(x):
859
+ 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
860
+ return f
861
+
862
+ def Perm_d_beta(x, d=2, beta=4):
863
+ f = 0.0
864
+ for i in range(d):
865
+ for j in range(d):
866
+ f += ((j+1) + beta)*(x[j]**(i+1) - (1/(j+1)**(i+1)))**2
867
+ return f
868
+
869
+ def Rotated_Hyper_Ellipsoid(x, d=2):
870
+ f = 0.0
871
+ for i in range(d):
872
+ for j in range(i+1):
873
+ f += x[j]**2
874
+ return f
875
+
876
+ def Sum_of_Different_Powers(x, d=2):
877
+ f = 0.0
878
+ for i in range(d):
879
+ f += np.abs(x[i])**i+1+1
880
+ return f
881
+
882
+ def SUM_SQUARES(x, d=2):
883
+ f = 0.0
884
+ for i in range(d):
885
+ f += (i+1)*x[i]**2
886
+ return f
887
+
888
+ def TRID(x, d=2):
889
+ sum_1 = 0.0
890
+ sum_2 = 0.0
891
+ for i in range(d):
892
+ sum_1 += (x[i] - 1)**2
893
+ for i in range(d-1):
894
+ sum_2 += x[i+1]*x[i]
895
+ f = sum_1 + sum_2
896
+ return f
897
+
898
+ def BOOTH(x):
899
+ f = (x[0] + 2*x[1] -7)**2 + (2*x[0] + x[1] - 5)**2
900
+ return f
901
+
902
+ def Matyas(x):
903
+ f = 0.26*(x[0]**2 + x[1]**2) - 0.48*x[0]*x[1]
904
+ return f
905
+
906
+ def MCCORMICK(x):
907
+ f = np.sin(x[0] + x[1]) + (x[0] - x[1])**2 - 1.5*x[0] + 2.5*x[1] + 1
908
+ return f
909
+
910
+ def Power_Sum(x, d=2, b=[8, 18, 44, 114]):
911
+ f = 0.0
912
+ for i in range(d):
913
+ sum_1 = 0.0
914
+ for j in range(d):
915
+ sum_1 += x[j]**(i+1)
916
+ f += (sum_1 - b[i])**2
917
+ return f
918
+
919
+ def Zakharov(x, d=2):
920
+ f = 0.0
921
+ sum_1 = 0.0
922
+ sum_2 = 0.0
923
+ sum_3 = 0.0
924
+ for i in range(d):
925
+ sum_1 += x[i]**2
926
+ sum_2 += 0.5*(i+1)*x[i]
927
+ sum_3 += 0.5*(i+1)*x[i]
928
+ f = sum_1 + sum_2**2 + sum_3**4
929
+ return f
930
+
931
+ def THREE_HUMP_CAMEL(x):
932
+ f = 2*x[0]**2 - 1.05*x[0]**4 + (x[0]**6/6) + x[0]*x[1] + x[1]**2
933
+ return f
934
+
935
+ def SIX_HUMP_CAMEL(x):
936
+ 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
937
+ return f
938
+
939
+ def DIXON_PRICE(x, d=2):
940
+ sum_1 = 0.0
941
+ for i in range(d-1):
942
+ i = i + 1
943
+ sum_1 += (i+1)*(2*x[i]**2 - x[i-1])**2
944
+ f = (x[0] - 1)**2 + sum_1
945
+ return f
946
+
947
+ def ROSENBROCK(x, d=2):
948
+ f = 0.0
949
+ for i in range(d-1):
950
+ f += 100*(x[i+1] - x[i]**2)**2 + (x[i] - 1)**2
951
+ return f
952
+
953
+ def DE_JONG(x):
954
+ 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],
955
+ [-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]]
956
+ sum_1 = 0.0
957
+ for i in range(25):
958
+ sum_1 += 1/((i+1) + (x[0] - a[0][i])**6 + (x[1] - a[1][i])**6)
959
+ f = (0.002 + sum_1)**(-1)
960
+ return f
961
+
962
+ def MICHALEWICZ(x, d=2, m=10):
963
+ f = 0.0
964
+ for i in range(d):
965
+ f += np.sin(x[i])*np.sin(((i+1)*x[i]**2)/np.pi)**(2*m)
966
+ f = -f
967
+ return f
968
+
969
+ def BEALE(x):
970
+ 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
971
+ return f
972
+
973
+ def BRANIN(x):
974
+ a=1
975
+ b=5.1/(4*(np.pi)**2)
976
+ c = 5/np.pi
977
+ r = 6
978
+ s = 10
979
+ t = 1/(8*np.pi)
980
+ f = a*(x[1] - b*x[0]**2 + c*x[0] - r)**2 + s*(1 - t)*np.cos(x[0]) + s
981
+ return f
982
+
983
+ def GOLDSTEIN_PRICE(x):
984
+ 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))
985
+ return f
986
+
987
+ def PERM_D_BETA(x, d=2, beta=1):
988
+ f = 0.0
989
+ for i in range(d):
990
+ sum_1 = 0
991
+ for j in range(d):
992
+ sum_1 += ((j+1)**(i+1) + beta)*((x[j]/(j+1))**(i+1) - 1)
993
+ f += sum_1
994
+ return f
995
+
996
+ class test_functions():
997
+ def __init__(self) -> None:
998
+ self.Ackley_ = {'name':'Ackley', 'f':Ackley, 'bounds':[[-32.768, 32.768], [-32.768, 32.768]], 'opt':[[0.0, 0.0], 0.0]}
999
+ 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]}
1000
+ 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]}
1001
+ self.Drop_Wave_ = {'name':'Drop_Wave', 'f':Drop_Wave, 'bounds':[[-5.12, 5.12], [-5.12, 5.12]], 'opt':[[0, 0], -1.0]}
1002
+ self.Eggholder_ = {'name':'Eggholder', 'f':Eggholder, 'bounds':[[-512.0, 512.0], [-512.0, 512.0]], 'opt':[[512.404, 512.404], -959.6407]}
1003
+ self.Griewank_ = {'name':'Griewank', 'f':Griewank, 'bounds':[[-600.0, 600.0], [-600.0, 600.0]], 'opt':[[0.0, 0.0], 0.0]}
1004
+ 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]}
1005
+ self.Levy_ = {'name':'Levy', 'f':Levy, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[1.0, 1.0], 0.0]}
1006
+ self.Rastrigin_ = {'name':'Rastrigin', 'f':Rastrigin, 'bounds':[[-5.12, 5.12], [-5.12, 5.12]], 'opt':[[0.0, 0.0], 0.0]}
1007
+ 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]}
1008
+ 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]}
1009
+ self.Schwefel_ = {'name':'Schwefel', 'f':Schwefel, 'bounds':[[-500.0, 500.0], [-500.0, 500.0]], 'opt':[[420.9687, 420.9687], 0.0]}
1010
+ self.Shubert_ = {'name':'Shubert', 'f':Shubert, 'bounds':[[-10.0, 10.0], [-10.0, 10.0]], 'opt':[[0.0, 0.0], -186.7309]}
1011
+ self.Styblinski_Tang_ = {'name':'Styblinski_Tang', 'f':Styblinski_Tang, 'bounds':[[-5, 5], [-5, 5]]}
1012
+ self.Easom_ = {'name':'Easom', 'f':Easom, 'bounds':[[-3, 3], [-3, 3]]}
1013
+ self.Bohachevsky_ = {'name':'Bohachevsky', 'f':Bohachevsky, 'bounds':[[-100.0, 100.0], [-100.0, 100.0]], 'opt':[[0, 0], 0]}
1014
+ 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]}
1015
+ 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]}
1016
+ 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]}
1017
+ self.SUM_SQUARES_ = {'name':'SUM_SQUARES', 'f':SUM_SQUARES, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[0.0, 0.0], 0]}
1018
+ self.TRID_ = {'name':'TRID', 'f':TRID, 'bounds':[[-4, 4], [-4, 4]], 'opt':[[2, 2], -2]}
1019
+ self.BOOTH_ = {'name':'BOOTH', 'f':BOOTH, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[1, 3], 0]}
1020
+ self.Matyas_ = {'name':'Matyas', 'f':Matyas, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[0, 0], 0]}
1021
+ self.MCCORMICK_ = {'name':'MCCORMICK', 'f':MCCORMICK, 'bounds':[[-1.5, 4], [-3, 4]], 'opt':[[-0.54719, -1.54719], -1.9133]}
1022
+ self.Power_Sum_ = {'name':'Power_Sum', 'f':Power_Sum, 'bounds':[[0, 2], [0, 2]]}
1023
+ self.Zakharov_ = {'name':'Zakharov', 'f':Zakharov, 'bounds':[[-5, 10], [-5, 10]], 'opt':[[0.0, 0.0], 0.0]}
1024
+ self.THREE_HUMP_CAMEL_ = {'name':'THREE_HUMP_CAMEL', 'f':THREE_HUMP_CAMEL, 'bounds':[[-5, 5], [-5, 5]], 'opt':[[0.0, 0.0], 0.0]}
1025
+ self.SIX_HUMP_CAMEL_ = {'name':'SIX_HUMP_CAMEL', 'f':SIX_HUMP_CAMEL, 'bounds':[[-3, 3], [-2, 2]], 'opt':[[0.0898, -0.7126], -1.0316]}
1026
+ self.DIXON_PRICE_ = {'name':'DIXON_PRICE', 'f':DIXON_PRICE, 'bounds':[[-10, 10], [-10, 10]], 'opt':[[1, 1/np.sqrt(2)], 0]}
1027
+ self.ROSENBROCK_ = {'name':'ROSENBROCK', 'f':ROSENBROCK, 'bounds':[[-5, 10], [-5, 10]], 'opt':[[1, 1], 0]}
1028
+ self.DE_JONG_ = {'name':'DE_JONG', 'f':DE_JONG, 'bounds':[[-65.536, 65.536], [-65.536, 65.536]]}
1029
+ self.MICHALEWICZ_ = {'name':'MICHALEWICZ', 'f':MICHALEWICZ, 'bounds':[[0, np.pi], [0, np.pi]], 'opt':[[2.2, 1.57], -1.8013]}
1030
+ self.BEALE_ = {'name':'BEALE', 'f':BEALE, 'bounds':[[-4.5, 4.5], [-4.5, 4.5]], 'opt':[[3, 0.5], 0]}
1031
+ self.BRANIN_ = {'name':'BRANIN', 'f':BRANIN, 'bounds':[[-5, 10], [0, 15]], 'opt':[[-np.pi, 12.275], 0.397887]}
1032
+ self.GOLDSTEIN_PRICE_ = {'name':'GOLDSTEIN_PRICE', 'f':GOLDSTEIN_PRICE, 'bounds':[[-2, 2], [-2, 2]], 'opt':[[0, -1], 3]}
1033
+ self.PERM_D_BETA_ = {'name':'PERM_D_BETA', 'f':PERM_D_BETA, 'bounds':[[-2, 2], [-2, 2]], 'opt':[[1, 2], 0]}
1034
+ self.dictionary = {'Ackley': self.Ackley_,
1035
+ 'sixth_Bukin':self.sixth_Bukin_,
1036
+ 'Cross_in_Tray':self.Cross_in_Tray_,
1037
+ 'Drop_Wave':self.Drop_Wave_,
1038
+ 'Eggholder':self.Eggholder_,
1039
+ 'Griewank':self.Griewank_,
1040
+ 'Holder_Table':self.Holder_Table_,
1041
+ 'Levy':self.Levy_,
1042
+ 'Rastrigin':self.Rastrigin_,
1043
+ 'second_Schaffe':self.second_Schaffe_,
1044
+ 'fourth_Schaffer':self.fourth_Schaffer_,
1045
+ 'Schwefel':self.Schwefel_,
1046
+ 'Shubert':self.Shubert_,
1047
+ 'Styblinski_Tang':self.Styblinski_Tang_,
1048
+ 'Easom':self.Easom_,
1049
+ 'Bohachevsky':self.Bohachevsky_,
1050
+ 'Perm_d_beta':self.Perm_d_beta_,
1051
+ 'Rotated_Hyper_Ellipsoid':self.Rotated_Hyper_Ellipsoid_,
1052
+ 'Sum_of_Different_Powers': self.Sum_of_Different_Powers_,
1053
+ 'SUM_SQUARES':self.SUM_SQUARES_,
1054
+ 'TRID':self.TRID_,
1055
+ 'BOOTH': self.BOOTH_,
1056
+ 'Matyas':self.Matyas_,
1057
+ 'MCCORMICK': self.MCCORMICK_,
1058
+ 'Power_Sum':self.Power_Sum_,
1059
+ 'Zakharov':self.Zakharov_,
1060
+ 'THREE_HUMP_CAMEL' :self.THREE_HUMP_CAMEL_,
1061
+ 'SIX_HUMP_CAMEL': self.SIX_HUMP_CAMEL_,
1062
+ 'DIXON_PRICE': self.DIXON_PRICE_,
1063
+ 'ROSENBROCK_': self.ROSENBROCK_,
1064
+ 'DE_JONG':self.DE_JONG_,
1065
+ 'MICHALEWICZ': self.MICHALEWICZ_,
1066
+ 'BEALE':self.BEALE_,
1067
+ 'BRANIN': self.BRANIN_,
1068
+ 'GOLDSTEIN_PRICE':self.GOLDSTEIN_PRICE_,
1069
+ 'PERM_D_BETA':self.PERM_D_BETA_}
1070
+ self.whole_list = list(self.dictionary.keys())
1071
+
1072
+
1073
+ def plotly_graph(problem, df=None):
1074
+ function=problem['f']
1075
+ x_lb=problem['bounds'][0][0]
1076
+ x_ub=problem['bounds'][0][1]
1077
+ y_lb=problem['bounds'][1][0]
1078
+ y_ub=problem['bounds'][1][1]
1079
+
1080
+ x = np.linspace(x_lb, x_ub, 100)
1081
+ y = np.linspace(y_lb, y_ub, 100)
1082
+ z = np.empty((100, 100))
1083
+ for ind_y, j in enumerate(y):
1084
+ for ind_x, i in enumerate(x):
1085
+ z[ind_y][ind_x] = function(np.array([i, j]))
1086
+
1087
+ steps_ = int(np.max(df['iter']))
1088
+
1089
+ fig1 = go.Figure(data=[go.Surface(x=x, y=y, z=z)])
1090
+ for step in range(steps_):
1091
+ points = df[df['iter']==step]
1092
+ points_x = list(points['x1'])
1093
+ points_y = list(points['x2'])
1094
+ points_z = list(points['f'])
1095
+ 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")))
1096
+ fig1.update_layout(title=f"f = {step}")
1097
+
1098
+ # Create figure
1099
+ fig = go.Figure(data=[go.Scatter3d(x=[], y=[], z=[],
1100
+ mode="markers",
1101
+ marker=dict(size=5, color="white", line=dict(width=1, color="black"))
1102
+ ), fig1.data[0]]
1103
+ )
1104
+
1105
+ # Frames
1106
+ frames = [go.Frame(data=[go.Scatter3d(x=k['x'],
1107
+ y=k['y'],
1108
+ z=k['z']
1109
+ ), fig1.data[0]
1110
+ ],
1111
+ traces= [0],
1112
+ name=f'frame{ind}'
1113
+ ) for ind, k in enumerate(fig1.data[1:])
1114
+ ]
1115
+
1116
+ fig.update(frames=frames)
1117
+
1118
+ def frame_args(duration):
1119
+ return {
1120
+ "frame": {"duration": duration},
1121
+ "mode": "immediate",
1122
+ "fromcurrent": True,
1123
+ "transition": {"duration": duration, "easing": "linear"},
1124
+ }
1125
+
1126
+
1127
+ sliders = [
1128
+ {"pad": {"b": 10, "t": 60},
1129
+ "len": 0.9,
1130
+ "x": 0.1,
1131
+ "y": 0,
1132
+
1133
+ "steps": [
1134
+ {"args": [[f.name], frame_args(0)],
1135
+ "label": str(k),
1136
+ "method": "animate",
1137
+ } for k, f in enumerate(fig.frames)
1138
+ ]
1139
+ }
1140
+ ]
1141
+
1142
+ fig.update_layout(
1143
+
1144
+ updatemenus = [{"buttons":[
1145
+ {
1146
+ "args": [None, frame_args(150)],
1147
+ "label": "Play",
1148
+ "method": "animate",
1149
+ },
1150
+ {
1151
+ "args": [[None], frame_args(150)],
1152
+ "label": "Pause",
1153
+ "method": "animate",
1154
+ }],
1155
+
1156
+ "direction": "left",
1157
+ "pad": {"r": 10, "t": 70},
1158
+ "type": "buttons",
1159
+ "x": 0.1,
1160
+ "y": 0,
1161
+ }
1162
+ ],
1163
+ sliders=sliders
1164
+ )
1165
+
1166
+ fig.update_layout(sliders=sliders)
1167
+ fig.write_html('animation.html')
1168
+ return fig
1169
+
1170
+
1171
+ #problem=test_functions().PERM_D_BETA_
1172
+ #df, best_var, best_sol = custom_pso(problem, 20, 2, maxiter=100, solver="Coulomb", k=0.00000000000000001, G=0.00000001, t=1.0, max_q=0.01, auto_reduce_search_space=True, dinamic=True)
1173
+ #plotly_graph(problem, df)
1174
+
1175
+ #print(best_sol)
1176
+
1177
+ ```
1178
+ ''')
1179
+
1180
+ st.markdown("### 4. Experimentaci贸n")
1181
+
1182
+ if "results" not in st.session_state:
1183
+ st.session_state.results = {'df':[], 'fig':[], 'best_solution':None, 'best_f':None}
1184
+
1185
+ c1, c2 = st.columns(2)
1186
+ options = test_functions().whole_list
1187
+ selected = c1.selectbox("choose problem", options, index=1)
1188
+ problem = test_functions().dictionary[selected]
1189
+ solver_list = ['Random', 'Newton', 'Coulomb']
1190
+ solver_selected = c2.selectbox("choose solver", options=solver_list, index=0)
1191
+
1192
+ st.markdown("Hyperparameters")
1193
+ # hyperparameters
1194
+ auto_constaint_choosen=False
1195
+ G=0.0000000001
1196
+ k=0.0000000001
1197
+ t=1.0
1198
+ options_as_mt=['Linear', 'Quadratic', 'Exponential']
1199
+ assing_method='Linear'
1200
+ if solver_selected == "Random":
1201
+ auto_constaint = st.selectbox("auto_constraint_domain", options=['False', 'True'], index=1)
1202
+ dic_auto_constaint = {'False':False, 'True':True}
1203
+ auto_constaint_choosen=dic_auto_constaint[auto_constaint]
1204
+
1205
+ elif solver_selected == "Newton":
1206
+ c1, c2, c3 = st.columns(3)
1207
+ G=c1.number_input("G", min_value=0.0, max_value=8.0, value=0.0000000001)
1208
+ assing_method = c2.selectbox("assing mehod", options=options_as_mt, index=0)
1209
+ t=c3.number_input("t", min_value=0.01, max_value=10.0, value=1.0)
1210
+
1211
+ elif solver_selected == "Coulomb":
1212
+ c1, c2, c3 = st.columns(3)
1213
+ k=c1.number_input("k", min_value=0.0, max_value=8.0, value=0.0000000001)
1214
+ assing_method = c2.selectbox("assing mehod", options=options_as_mt, index=0)
1215
+ t=c3.number_input("t", min_value=0.01, max_value=10.0, value=1.0)
1216
+
1217
+ solve_bt = st.button("Solve")
1218
+ if solve_bt:
1219
+ df, best_solution, best_f = custom_pso(problem, nPart=20, nVar=2, maxiter=100, solver=solver_selected, auto_reduce_search_space=auto_constaint_choosen, G=G, t=t, keep_best=True, k=k, dinamic=True, weight_method=assing_method)
1220
+ fig = plotly_graph(problem, df)
1221
+ st.session_state.results['df'], st.session_state.results['best_solution'], st.session_state.results['best_f'], st.session_state.results['fig'] = df, best_solution, best_f, fig
1222
+
1223
+ if len(st.session_state.results['df'])>0:
1224
+ results = st.session_state.results
1225
+ df, best_solution, best_f = results['df'], results['best_solution'], results['best_f']
1226
+ with st.expander('Performance'):
1227
+ c1, c2 = st.columns([4, 1])
1228
+ c1.dataframe(df)
1229
+ c2.markdown('Best Solution:')
1230
+ df = pd.DataFrame(best_solution)
1231
+ df.columns = ['value']
1232
+ c2.dataframe(df)
1233
+ c2.markdown(f'Best Value: `{best_f}`')
1234
+ fig = results['fig']
1235
+ st.plotly_chart(fig)