Spaces:
Runtime error
Runtime error
Create app.py
Browse files
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 |
+
Email: [email protected]
|
| 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)
|