library(GA) #Algoritmos Evolutivos
## Cargando paquete requerido: foreach
## Cargando paquete requerido: iterators
## Package 'GA' version 3.2.4
## Type 'citation("GA")' for citing this R package in publications.
##
## Adjuntando el paquete: 'GA'
## The following object is masked from 'package:utils':
##
## de
library(pso) #Optimización de Particulas
library(DEoptim) #Evolución Diferencial
## Cargando paquete requerido: parallel
##
## DEoptim package
## Differential Evolution algorithm in R
## Authors: D. Ardia, K. Mullen, B. Peterson and J. Ulrich
library(dplyr)
##
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(ggplot2)
library(plotly)
##
## Adjuntando el paquete: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
library(gganimate)
library(knitr)
library(gifski)
library(viridis)
## Cargando paquete requerido: viridisLite
library(htmlwidgets)
En este primer punto se estudian y comparan distintos métodos de optimización aplicados a funciones no lineales. El objetivo es analizar el desempeño de algoritmos clásicos como el descenso por gradiente frente a métodos heurísticos inspirados en procesos evolutivos y naturales. Para ello, se seleccionaron dos funciones de prueba con características distintas que permiten evaluar la eficacia y eficiencia de cada enfoque. Se realizarán optimizaciones en espacios de dos y tres dimensiones, partiendo de condiciones iniciales aleatorias. Además, se visualizará el proceso de búsqueda de soluciones mediante animaciones.Para finalmente comparar como fue el comportamiento de los métodos y como el tipo de función impacta en la elección del método.
La función de Rastrigin se define como:
\[ f(\mathbf{x}) = A n + \sum_{i=1}^{n} \left[ x_i^2 - A \cos(2 \pi x_i) \right] \]
Donde, \(\mathbf{x} = (x_1, x_2, \ldots, x_n) \in \mathbb{R}^n\)
rastrigin <- function(x, A = 10) {
n <- length(x)
A * n + sum(x^2 - A * cos(2 * pi * x))
}
# RASTRIGIN 1-D
# Este bloque dibuja la función Rastrigin en una sola variable
# y marca con puntos rojos los mínimos locales situados
# en cada número entero entre −5 y +5.
# 1) Convertimos la función ‘rastrigin()’ a una versión que
# acepte un vector de x y devuelva un vector de resultados.
# Vectorize() hace eso automáticamente.
rastrigin_1d <- Vectorize(function(x) rastrigin(x))
# 2) Dibujamos la curva:
# • from / to → rango del eje X
# • n → número de puntos que se calculan
# • xlab, ylab → etiquetas de los ejes
# • main → título del gráfico
curve(
rastrigin_1d(x), # la función a pintar
from = -5.12, to = 5.12, # límites de X
n = 1000, # resolución del trazo
xlab = "x", # texto en eje X
ylab = "f(x)", # texto en eje Y
main = "Rastrigin 1D — campo de pozos periódicos"
)
# 3) Añadimos PUNTOS ROJOS en cada entero (… −5, −4, …, 4, 5)
# para resaltar los mínimos locales de la función.
points(
-5:5, # posiciones X de los puntos
rastrigin_1d(-5:5), # alturas Y = f(x) en esas X
pch = 19, # tipo de símbolo (19 = punto sólido)
col = "red" # color de los puntos
)
Fig 1. Visualización de la función Rastrigin f(x1) (1D)
| Elemento visual | Observación | Interpretación |
|---|---|---|
| Línea negra dentada | Ondas regulares de amplitud creciente al alejarse del centro. | Resultado de la combinación \(x^2+cos(2\pi x)\). El término cuadrático empuja hacia arriba (crece como cuenco), el coseno introduce la ondulación periódica. |
| Puntos rojos (11) | Un punto rojo en cada entero desde −5 hasta 5. | Representan los mínimos locales: los pozos donde un optimizador basado solo en gradientes podría quedar atrapado. |
| Valle más profundo en \(x=0\) | Punto rojo más bajo (\(f(x)=0\)). | Mínimo global de la función. |
| Simetría izquierda-derecha | La curva es espejo respecto a \(x=0\). | Tanto \(x^2\) como \(cos(2\pi x)\) son funciones pares → la función es simétrica. |
| Incremento de altura hacia los extremos | Las puntas laterales alcanzan \(f(x)\approx40\) . | Dominio del término \(x^2\). Cuanto más lejos del centro, más alto el coste. |
Alta multimodalidad
Once pozos en un intervalo de solo 10 unidades indican que la función
está plagada de óptimos locales.
Periodocidad
La distancia constante entre picos (\(\approx
1\)) viene del \(cos(2\pi x)\);
cada ciclo completo genera un pozo.
# RASTRIGIN 2-D
# Objetivo: dibujar un “mapa de calor” que muestre los pozos locales
# y el mínimo global de la función Rastrigin en dos variables.
# 1. Crear la rejilla (grilla) de puntos
# • seq() genera 200 valores igualmente espaciados
# • El rango va de −5.12 a +5.12 (dominio típico de prueba)
x <- y <- seq(-5.12, 5.12, length.out = 200)
# 2. Calcular f(x1,x2) para cada punto de la rejilla
# • outer() combina todos los x con todos los y
# • Vectorize() convierte la llamada a rastrigin() en “apta para matriz”
z <- outer(
x, y,
Vectorize(function(a, b) rastrigin(c(a, b))) # a = x1, b = x2
)
# 3. Dibujar el contorno coloreado
filled.contour(
x, y, z,
color.palette = terrain.colors, # paleta “relieve” (verde-marrón)
nlevels = 30, # número de franjas de color
plot.title = title(
main = "Rastrigin 2-D — campo de minas", # título grande
xlab = "x1", # etiqueta eje X
ylab = "x2" # etiqueta eje Y
),
# --- plot.axes: todo lo que añadas aquí se superpone al gráfico ---
plot.axes = {
axis(1); axis(2) # muestra los ejes
abline(h = -5:5, v = -5:5, # rejilla entera
col = "grey90", lty = 3) # líneas gris claro
points(0, 0, # coord. (0,0)
pch = 8, # símbolo estrella
col = "black",
cex = 1.5) # tamaño 1.5×
}
)
Fig 2. Visualización de la función Rastrigin f(x1, x2) (2D)
Elemento visualGradiente de color (barra a la derecha) |
Qué se observaVerde muy oscuro → Valores cercanos a 0. |
Qué significaIndica la altura de la función \(f(x_1,x_2)\). Los colores claros son “cerros”; los oscuros, “valles”. |
| Estrella negra en (0, 0) | Único punto marcado en el centro. | Mínimo global: \(f(0,0) = 0\). Cualquier optimizador querrá llegar aquí. |
| Malla gris punteada | Líneas cada unidad en ambos ejes. | Subraya que la función es separable y periódica: los mínimos locales se repiten exactamente en cada entero. |
| Óvalos verdes/amarillos repetidos | Patrón regular horizontal y vertical. | Cada óvalo es un pozo local; la cuadrícula entera corresponde a la estructura \(cos(2\pi x_i)\) de Rastrigin. |
Alta multimodalidad
Hay decenas de valles; un descenso por gradiente típico corre riesgo de
quedar atrapado en cualquiera de ellos.
Separabilidad
Las líneas punteadas muestran que los mínimos están exactamente en los
cruces de enteros (x1,x2)∈Z2. La función puede verse como la suma de dos
copias 1-D independientes.
Incremento radial suave
A medida que nos alejamos del centro, los colores se vuelven lentamente
rosados
“Campo de minas”
Optimizar aquí es como cruzar un terreno lleno de cráteres: cada
cuadrícula contiene un pozo que puede atrapar al algoritmo.
# -----------------------------------------------
# Dibujar superficie 3D de Rastrigin y mínimo global
# -----------------------------------------------
# Evaluar el mínimo global
min_x <- 0
min_y <- 0
min_z <- 20 # Para Rastrigin 2D, f(0, 0) = 20
# Crear el gráfico y guardar la transformación
trans <- persp(
x, y, z,
theta = 60, # Ángulo horizontal (más frontal)
phi = 35, # Ángulo vertical (más inclinada hacia abajo)
expand = 0.6,
col = "lightblue",
shade = 0,
border = NA,
ticktype = "simple",
xlab = "x1",
ylab = "x2",
zlab = "f(x)",
main = "Rastrigin 2D — Superficie 3D con mínimo global"
)
# Proyectar el punto (0, 0, f(0,0)) a 2D para añadirlo al gráfico
punto <- trans3d(min_x, min_y, min_z, trans)
# Añadir punto rojo
points(punto, col = "salmon", pch = 16, cex = 2) # pch 16 = círculo lleno
Fig 3. Visualización de la función Rastrigin f(x1, x2, x3) (3D)
Se aprecia un cuenco global con un mosaico de mínimos locales
periódicos.
El mínimo global está en (0,0) y las crestas alcanzan valores de \(f(\mathbf{x})=80\).
Implicaciones para la optimización
Gradiente “traicionero”
Desde un punto alto, el gradiente guía hacia el valle más
cercano, no el global; por eso un descenso por gradiente
sencillo suele frenarse en uno de esos cráteres periféricos.
Incremento radial suave + oscilación fina
La combinación hace que los valles laterales sean relativamente
profundos: un algoritmo necesita capacidad de exploración para
saltar varias crestas y acercarse al centro.
Métodos locales (GD, Newton) requieren muchos reinicios o mejoras (momentum, annealing).
Estrategias bioinspiradas (PSO, hormigas) o cuasi-Newton (L-BFGS-B) suelen rendir mejor, porque pueden escapar de pozos.
grad_rastrigin <- function(x, A = 10) {
2 * x + 2 * pi * A * sin(2 * pi * x)
}
# Función GD simple con historial
gd_once <- function(x0, lr = 0.01, iters = 200) {
x <- x0
hist <- matrix(NA, nrow = iters, ncol = length(x0) + 1) # guarda x1, x2, f(x)
for (k in 1:iters) {
g <- grad_rastrigin(x) # gradiente
x <- x - lr * g # paso GD
hist[k, ] <- c(x, rastrigin(x)) # guardar estado
}
colnames(hist) <- c(paste0("x", 1:length(x0)), "f")
return(as.data.frame(hist))
}
# GRÁFICO 1-D CON GD_PURO
# 1. Ejecutar el descenso por gradiente
x0 <- 3 # punto de partida
traj1d <- gd_once(x0, lr = 0.01, iters = 200)
# 2. Añadir la columna de iteración
traj1d$iter <- seq_len(nrow(traj1d)) - 1 # 0, 1, 2, …
# 3. Graficar Rastrigin 1-D y la trayectoria
rastrigin_v <- Vectorize(rastrigin)
curve(rastrigin_v(x), from = -5.12, to = 5.12, n = 1000,
xlab = "x", ylab = "f(x)",
main = "Trayectoria de GD puro en Rastrigin 1-D")
# Línea azul con el camino del algoritmo
points(traj1d$x1, traj1d$f, type = "l",
col = "dodgerblue", lwd = 2)
# Puntos de inicio (rojo) y final (negro)
points(x0, rastrigin(x0), pch = 19, col = "red") # inicio
points(tail(traj1d$x1, 1), tail(traj1d$f, 1),
pch = 17, col = "black") # fin
Fig 4. Visualización de la función Rastrigin óptimo en f(x1) (1D)
Color / Símbolo▲ Negro |
Significado correctoPunto inicial \(\mathbf{x_0}\approx2.9\). El descenso por gradiente parte aquí, en la ladera alta del valle. |
| 🔴 Rojo | Punto final: mínimo local alcanzado tras 200 iteraciones, \(x\approx2.8\)x≈2.8 con \(14\lessapprox f(\mathbf{x})\lessapprox15\) |
| Zona azul | Todos los pasos intermedios; muestra cómo GD oscila dentro del mismo valle hasta estabilizarse en el punto rojo. |
Descenso rápido
El algoritmo parte del punto negro (valor f≈27) y se
desliza cuesta abajo siguiendo el gradiente.
Oscilaciones amortiguadas
En azul se ve cómo va rebotando a ambos lados del eje del valle. Cada
rebote es más pequeño porque el gradiente se reduce a medida que se
acerca al fondo.
Convergencia local, fracaso global:
El algoritmo se detiene en el punto rojo, el fondo del
pozo local. Allí la pendiente es prácticamente nula y se cumple el
criterio de paro. Queda atrapado en el primer mínimo
local encontrado.
Influencia del punto de partida: cualquier inicio en el intervalo \([2.5,\ 3.5]\) produciría resultados parecidos; reiniciar desde zonas distintas sólo trasladaría el problema a otros pozos.
Distancia al óptimo global
El óptimo global de Rastrigin (\(\mathbf{x}=0,
f=0\)) queda fuera de la zona azul; GD nunca lo “visita”. Esto
evidencia la limitación de los métodos locales en funciones
multimodales.
Necesidad de estrategias complementarias: para alcanzar el óptimo global en Rastrigin se requieren técnicas que permitan “saltar” valles (momentum, tasa de aprendizaje adaptativa, meta-heurísticas globales).
# Malla (superficie de referencia) --------------------------
x_vals <- y_vals <- seq(-5.12, 5.12, length.out = 200)
contour_df <- expand.grid(x1 = x_vals, x2 = y_vals)
contour_df$z <- mapply(
function(a, b) rastrigin(c(a, b)),
contour_df$x1, contour_df$x2
)
# Ejecutar Gradient Descent puro ----------------------------
# · sin argumentos “max_iter” ni “tol” → usamos ‘iters’
gd_res <- gd_once(c(3, 3), lr = 0.01, iters = 200)
# Construir data frame con las columnas que ggplot necesita
traj_df <- data.frame(
x1 = gd_res$x1, # posiciones en eje X
x2 = gd_res$x2, # posiciones en eje Y
f = gd_res$f, # valor de la función
iter = seq_len(nrow(gd_res)) - 1 # contador de iteraciones 0…199
)
# Definir los puntos de Inicio y Fin para la leyenda -------
puntos_df <- data.frame(
x1 = c(traj_df$x1[1], tail(traj_df$x1, 1)),
x2 = c(traj_df$x2[1], tail(traj_df$x2, 1)),
tipo = factor(c("Fin", "Inicio"), levels = c("Fin", "Inicio"))
)
# Gráfico estático ------------------------------------------------
ggplot() +
# Fondo: mapa de calor f(x)
geom_tile(data = contour_df,
aes(x1, x2, fill = z)) +
geom_contour(data = contour_df,
aes(x1, x2, z = z),
colour = "white", alpha = 0.25, bins = 25) +
scale_fill_viridis_c(option = "D", name = expression(f(x))) +
# Trayectoria
geom_path(data = traj_df,
aes(x1, x2),
colour = "dodgerblue", linewidth = 1.3) +
geom_point(data = traj_df[1, ], # punto inicial
aes(x1, x2),
colour = "black", size = 3) +
geom_point(data = tail(traj_df, 1), # punto final
aes(x1, x2),
colour = "red", size = 3) +
# Puntos de inicio y fin con leyenda automática
geom_point(data = puntos_df,
aes(x = x1, y = x2, colour = tipo, shape = tipo),
size = 3) +
scale_colour_manual(values = c("Inicio" = "black", "Fin" = "red"),
name = "") +
scale_shape_manual(values = c("Inicio" = 16, # círculo lleno
"Fin" = 17), # triángulo
name = "") +
# Detalles estéticos
labs(title = "Trayectoria de GD puro sobre Rastrigin 2D",
subtitle = "• Rojo = mínimo local donde se atasca",
x = expression(x[1]), y = expression(x[2])) +
theme_minimal(base_size = 14) +
theme(legend.position = "right",
plot.title = element_text(face = "bold"))
Fig 5. Visualización de la función Rastrigin óptimo f(x1, x2) (2D)
| Elemento | Significado |
|---|---|
| 🔵 Trayectoria azul | Secuencia de pasos que toma Gradient Descent desde el punto inicial. |
| ⚫ Círculo negro (Inicio) | Posición de partida \((3,\ 3)\) del algoritmo. |
| 🔺 Triángulo rojo (Fin) | Mínimo local donde GD queda atrapado tras 200 iteraciones. |
| Mapa de colores (f(x)) | Valor de la función Rastrigin; violeta oscuro (\(\approx0\)) (valles), amarillo (\(\approx 80\)) (picos). |
| Observación | Interpretación |
|---|---|
| El recorrido es muy corto y se queda en la parte superior-derecha. | GD se vio atraído por el valle local más próximo y no dispuso de mecanismos para “saltar” la siguiente cresta. |
| Valor final (\(f(\mathbf{x})=17\)) frente al mínimo global \(f(\mathbf{x})=0\). | Confirma el estancamiento prematuro en un óptimo local. |
| Trayectoria casi rectilínea hacia un solo pozo. | El gradiente inicial apunta con fuerza a la depresión más cercana; tras llegar al fondo la norma del gradiente se vuelve pequeña y el algoritmo se detiene. |
En funciones altamente multimodales como Rastrigin, el número de valles hace muy improbable que GD, partiendo lejos del origen, alcance el mínimo global.
# GRÁFICO Y ANIMACIÓN 2-D DE UNA ÚNICA TRAYECTORIA GD (sin
# reinicios) SOBRE LA FUNCIÓN RASTRIGIN.
# Datos de la superficie (contorno con leyenda f(x))
# ─────────────────────────────────────────────────────────────
x_vals <- y_vals <- seq(-5.12, 5.12, length.out = 200)
contour_df <- expand.grid(x1 = x_vals, x2 = y_vals) %>%
mutate(z = mapply(function(a, b) rastrigin(c(a, b)), x1, x2))
# Una sola trayectoria con gd_once()
# ─────────────────────────────────────────────────────────────
set.seed(42) # reproducibilidad
x0 <- c(3, 3) # punto inicial elegido
gd_res <- gd_once(x0, lr = 0.01, iters = 50)
traj_df <- gd_res %>%
mutate(iter = seq_len(n()) - 1) # 0,1,2,… para animación
# Animación 2-D de la misma trayectoria
# ─────────────────────────────────────────────────────────────
p_anim <- ggplot() +
geom_tile(data = contour_df,
aes(x1, x2, fill = z)) +
geom_contour(data = contour_df,
aes(x1, x2, z = z),
colour = "white", alpha = 0.3, bins = 30) +
scale_fill_viridis_c(name = expression(f(x)), option = "D") +
geom_path(data = traj_df,
aes(x1, x2),
colour = "red", linewidth = 1.3) +
geom_point(data = traj_df,
aes(x1, x2),
colour = "black", size = 2) +
labs(title = "Animación: trayectoria de GD puro (Rastrigin 2-D)",
subtitle = "Iteración: {frame_along}",
x = expression(x[1]), y = expression(x[2])) +
theme_minimal(base_size = 14) +
theme(legend.position = "right",
plot.title = element_text(face = "bold")) +
transition_reveal(along = iter) +
ease_aes("linear")
animate(p_anim,
nframes = max(traj_df$iter) + 1,
fps = 15,
width = 700,
height = 500,
units = "px",
renderer = gifski_renderer())
Fig 6. Animación de la función Rastrigin óptimo f(x1, x2) (2D)
Inicio en un pico elevado
Al “play”, el punto negro parte en una zona amarilla (valor alto de
\(f(\mathbf{x})\)), indicando un punto
inicial lejos del mínimo.
Descenso pronunciado
En las primeras iteraciones, el punto negro se desplaza rápidamente
hacia regiones más oscuras (valores de f decrecientes), cayendo dentro
de un pozo local.
Estabilización
Una vez dentro del valle, la trayectoria azul rodea el fondo con
pequeños vaivenes, reflejando zig-zags de GD puro en un entorno
multimodal.
Sin escape del mínimo local
A pesar de varias decenas de iteraciones, el punto nunca atraviesa la
cresta que llevaría al valle central
# — 1) Superficie de Rastrigin —
x_vals <- y_vals <- seq(-5.12, 5.12, length.out = 50)
z_matrix <- outer(x_vals, y_vals,
Vectorize(function(a,b) rastrigin(c(a,b))))
# — 2) Ejecución de GD desde (3,3) —
set.seed(42)
res <- gd_once(c(3, 3), lr = 0.01, iters = 50)
traj <- as.matrix(res[, c("x1","x2")]) # n × 2
fvals <- res$f # longitud n
iters <- seq_len(nrow(res)) - 1 # 0:(n-1)
# — 3) Óptimo local —
opt_idx <- which.min(fvals)
opt_point <- traj[opt_idx, ]
opt_val <- fvals[opt_idx]
# — 4) Construcción de frames CORREGIDA —
frames <- lapply(seq_along(iters), function(i) {
list(
data = list(
NULL, # trace 0: superficie (estático)
# trace 1: línea hasta iter i
list(
x = traj[1:i,1],
y = traj[1:i,2],
z = fvals[1:i],
type = "scatter3d", mode = "lines",
line = list(color=0:(i-1), colorscale="Rainbow",
cmin=0, cmax=max(iters), width=6)
),
# trace 2: punto móvil en iter i
list(
x = traj[i,1],
y = traj[i,2],
z = fvals[i],
type = "scatter3d", mode = "markers",
marker = list(size=6, color="black")
),
NULL # trace 3: óptimo (estático)
),
name = as.character(iters[i]),
traces = c(1,2) # animamos sólo línea y punto
)
})
# — 5) Figura inicial con 4 traces —
fig <- plot_ly() %>%
# trace 0: superficie
add_surface(
x = ~x_vals, y = ~y_vals, z = ~z_matrix,
colorscale = "Viridis", opacity = 0.2, showscale = FALSE
) %>%
# trace 1: línea (iter 0)
add_trace(
x = traj[1,1], y = traj[1,2], z = fvals[1],
type = "scatter3d", mode = "lines",
line = list(color=0, colorscale="Rainbow",
cmin=0, cmax=max(iters), width=6),
name = "Trayectoria"
) %>%
# trace 2: punto inicial
add_trace(
x = traj[1,1], y = traj[1,2], z = fvals[1],
type = "scatter3d", mode = "markers",
marker = list(size=6, color="black"),
name = "Punto actual"
) %>%
# trace 3: óptimo encontrado
add_trace(
x = opt_point[1], y = opt_point[2], z = opt_val,
type = "scatter3d", mode = "markers",
marker = list(size=10, symbol="diamond", color="yellow"),
name = "Óptimo encontrado"
) %>%
layout(
title = paste0("GD en Rastrigin 3D — Iteración 0 — f = ", round(fvals[1],4)),
scene = list(
xaxis = list(title = "x1", range = c(-5.12,5.12)),
yaxis = list(title = "x2", range = c(-5.12,5.12)),
zaxis = list(title = "f(x)"),
camera = list(
eye = list(x=0.05, y=1.50, z=0.30),
center = list(x=0,y=0,z=0),
up = list(x=0,y=0,z=1)
)
),
sliders = list(
list(
active = 0, pad = list(t=50),
currentvalue = list(prefix="Iteración: "),
steps = lapply(seq_along(iters), function(i) {
list(
method = "animate",
args = list(list(as.character(iters[i])),
list(mode="immediate",
frame=list(duration=100, redraw=TRUE))),
label = as.character(iters[i])
)
})
)
),
updatemenus = list(
list(
type="buttons", showactive=FALSE,
x=1, xanchor="right", y=0, yanchor="bottom",
buttons=list(
list(label="▶ Play", method="animate",
args=list(NULL, list(frame=list(duration=100,redraw=TRUE),
mode="immediate"))),
list(label="⏸ Pause", method="animate",
args=list(list(NULL), list(frame=list(duration=0,redraw=FALSE),
mode="immediate")))
)
)
)
)
# — 6) Inyecta frames y habilita autoplay —
fig$x$frames <- frames
fig <- fig %>% onRender("
function(el,x){
el.on('plotly_buttonclicked',function(e){
if(e.button && e.button.label==='▶ Play'){
Plotly.animate(el,null,{frame:{duration:100,redraw:true},mode:'immediate'});
}
});
}
")
# — 7) Guarda y muestra —
htmlwidgets::saveWidget(fig,
"rastrigin_gd_3d_zoom_maximo_correcto.html",
selfcontained = TRUE)
fig
Fig 7. Visualización de la función Rastrigin óptimo f(x1, x2, x3) (3D)
| Elemento | Significado |
|---|
| Superficie translúcida (Viridis) | Muestra el paisaje completo de la función Rastrigin: valles profundos (violeta) y picos altos (amarillo). |
| 🟠 Línea multicolor (“Trayectoria”) | Cada tonalidad marca un paso de GD: del azul al naranja conforme avanza la iteración. |
| ⚫ Punto negro (“Punto actual”) | Indica la posición de GD en la iteración mostrada. |
| Diamante amarillo (“Óptimo encontrado”) | Señala el mínimo local donde el algoritmo quedó atrapado. |
| ▶️⏸️ Controles Play/Pause + slider | Permiten navegar interactivamente por las iteraciones. | | | | | | | | |
Descenso inicial:
Desde el punto \((3,\ 3)\), GD baja
rápidamente por la “pared” del valle, capturado por el gradiente
pronunciado.
Ingreso en un cráter local:
Tras unas pocas iteraciones, la trayectoria desciende al fondo de un
pozo lateral, lejos del centro \((0,\
0)\).
Estancamiento y micro-oscillaciones:
Dentro de ese cráter, GD gira en pequeños zig-zags alrededor de un
mínimo que no es el global.
No alcanza el mínimo global:
El diamante amarillo permanece alejado del valle central de la
superficie, confirmando la captura en un mínimo local.
set.seed(420) # Semilla
GA_rastrigin_2d <- ga(
type = "real-valued",
fitness = function(x) -rastrigin(x), # GA maximiza, por eso usamos el negativo
lower = c(-5.12, -5.12), # Límites inferiores
upper = c(5.12, 5.12), # Límites superiores
popSize = 50, # Tamaño de la población
maxiter = 100, # Número máximo de iteraciones
run = 50 # Criterio de parada si no mejora
)
# Mostrar resultados
summary(GA_rastrigin_2d)
## ── Genetic Algorithm ───────────────────
##
## GA settings:
## Type = real-valued
## Population size = 50
## Number of generations = 100
## Elitism = 2
## Crossover probability = 0.8
## Mutation probability = 0.1
## Search domain =
## x1 x2
## lower -5.12 -5.12
## upper 5.12 5.12
##
## GA results:
## Iterations = 100
## Fitness function value = -4.038806e-05
## Solution =
## x1 x2
## [1,] -2.041459e-05 0.0004507303
library(GA)
library(magick)
## Linking to ImageMagick 6.9.12.98
## Enabled features: cairo, freetype, fftw, ghostscript, heic, lcms, pango, raw, rsvg, webp
## Disabled features: fontconfig, x11
# Crear una carpeta temporal para guardar las imágenes
dir_temp <- tempdir()
files <- character(0)
# Monitor que guarda cada frame como una imagen PNG
monitor_magick <- function(obj) {
iter <- obj@iter
pop <- obj@population
png_filename <- file.path(dir_temp, sprintf("frame_%03d.png", iter))
png(png_filename, width=600, height=600)
plot(pop, xlim=c(-5.12, 5.12), ylim=c(-5.12, 5.12),
main=paste("Iteración", iter),
xlab="x", ylab="y", pch=19, col=rgb(0,0,1,0.5))
dev.off()
files <<- c(files, png_filename)
}
set.seed(420)
GA_rastrigin_2d <- ga(
type = "real-valued",
fitness = function(x) -rastrigin(x),
lower = c(-5.12, -5.12),
upper = c(5.12, 5.12),
popSize = 50,
maxiter = 100,
run = 50,
monitor = monitor_magick
)
# Leer las imágenes y combinarlas en un GIF animado en memoria
img_list <- image_read(files)
animation <- image_animate(img_list, fps = 10)
# Crear carpeta de salida si no existe
output_dir <- "Trabajo1_files"
if (!dir.exists(output_dir)) dir.create(output_dir)
# Ruta del archivo GIF de salida
gifga_path <- file.path(output_dir, "rastrigin_ga_animation.gif")
# Guardar el GIF en disco
image_write(animation, gifga_path)
knitr::include_graphics(gifga_path)
Fig X. Animación Algoritmos evolutivos 2D
set.seed(420) # Semilla
GA_rastrigin_3d <- ga(
type = "real-valued",
fitness = function(x) -rastrigin(x), # GA maximiza, por eso usamos el negativo
lower = c(-5.12, -5.12, -5.12), # Límites inferiores
upper = c(5.12, 5.12, 5.12), # Límites superiores
popSize = 50, # Tamaño de la población
maxiter = 100, # Número máximo de iteraciones
run = 50 # Criterio de parada si no mejora
)
# Mostrar resultados
summary(GA_rastrigin_3d)
## ── Genetic Algorithm ───────────────────
##
## GA settings:
## Type = real-valued
## Population size = 50
## Number of generations = 100
## Elitism = 2
## Crossover probability = 0.8
## Mutation probability = 0.1
## Search domain =
## x1 x2 x3
## lower -5.12 -5.12 -5.12
## upper 5.12 5.12 5.12
##
## GA results:
## Iterations = 100
## Fitness function value = -0.01441897
## Solution =
## x1 x2 x3
## [1,] 0.006877056 0.004748523 -0.001686947
library(GA)
library(magick)
library(scatterplot3d)
library(rgl)
# Crear carpeta temporal para imágenes
dir_temp <- tempdir()
files <- character(0)
# Monitor para GA en 3D que guarda imágenes
monitor_magick_3d <- function(obj) {
iter <- obj@iter
pop <- obj@population
png_filename <- file.path(dir_temp, sprintf("frame_%03d.png", iter))
png(png_filename, width = 600, height = 600)
# Graficar la población en 3D
scatterplot3d(
pop[,1], pop[,2], pop[,3],
xlim = c(-5.12, 5.12),
ylim = c(-5.12, 5.12),
zlim = c(-5.12, 5.12),
pch = 19, color = rgb(0, 0, 1, 0.5),
xlab = "x", ylab = "y", zlab = "z",
main = paste("Iteración", iter)
)
points3d(0, 0, 0, col = "red", pch = 8) # Óptimo global
dev.off()
files <<- c(files, png_filename)
}
# Ejecutar el GA en 3D con el monitor modificado
set.seed(420)
GA_rastrigin_3d <- ga(
type = "real-valued",
fitness = function(x) -rastrigin(x),
lower = c(-5.12, -5.12, -5.12),
upper = c(5.12, 5.12, 5.12),
popSize = 50,
maxiter = 100,
run = 50,
monitor = monitor_magick_3d
)
# Crear GIF animado desde imágenes
img_list <- image_read(files)
animation <- image_animate(img_list, fps = 10)
# Guardar el GIF
output_dir <- "Trabajo1_files"
if (!dir.exists(output_dir)) dir.create(output_dir)
gifga3d_path <- file.path(output_dir, "rastrigin_ga_3d.gif")
image_write(animation, gifga3d_path)
knitr::include_graphics(gifga3d_path)
Fig X. Animación Algoritmos evolutivos 3D
pso_rastrigin_2d <- psoptim(
par = c(NA, NA), # Inicialización (puede ser aleatoria)
fn = rastrigin, # Función a minimizar
lower = c(-5.12, -5.12), # Límites inferiores
upper = c(5.12, 5.12), # Límites superiores
control = list(
maxit = 200, # Número de iteraciones
s = 40, # Tamaño del enjambre (partículas)
trace = 1 # Mostrar progreso
)
)
## S=40, K=3, p=0.07314, w0=0.7213, w1=0.7213, c.p=1.193, c.g=1.193
## v.max=NA, d=14.48, vectorize=FALSE, hybrid=off
## It 10: fitness=0.4549
## It 20: fitness=0.4549
## It 30: fitness=0.2394
## It 40: fitness=0.05595
## It 50: fitness=0.01652
## It 60: fitness=0.004545
## It 70: fitness=0.001865
## It 80: fitness=0.0001042
## It 90: fitness=3.171e-07
## It 100: fitness=2.789e-07
## It 110: fitness=2.817e-08
## It 120: fitness=2.386e-08
## It 130: fitness=1.183e-10
## It 140: fitness=1.183e-10
## It 150: fitness=3.268e-12
## It 160: fitness=2.487e-14
## It 170: fitness=2.132e-14
## It 180: fitness=2.132e-14
## It 190: fitness=1.066e-14
## It 200: fitness=0
## Maximal number of iterations reached
library(pso)
library(magick)
# Lista para guardar la historia del enjambre
pso_history <- list()
# Función wrapper con historial
rastrigin_with_history <- function(x) {
# Solo registrar si estamos dentro de PSO
if (exists(".current_iter", envir = .GlobalEnv)) {
i <- get(".current_iter", envir = .GlobalEnv)
assign(".pso_particles", rbind(get(".pso_particles", envir = .GlobalEnv), x), envir = .GlobalEnv)
}
rastrigin(x)
}
# Monitor manual en bucle
maxit <- 200
s <- 40
# Inicialización
assign(".pso_particles", matrix(, nrow = 0, ncol = 2), envir = .GlobalEnv)
# Ejecutar PSO y registrar enjambre por iteración
set.seed(42)
for (iter in 1:maxit) {
assign(".current_iter", iter, envir = .GlobalEnv)
assign(".pso_particles", matrix(, nrow = 0, ncol = 2), envir = .GlobalEnv)
psoptim(
par = c(NA, NA),
fn = rastrigin_with_history,
lower = c(-5.12, -5.12),
upper = c(5.12, 5.12),
control = list(
maxit = 1,
s = s,
maxfn = 0, # No queremos que evolucione, solo un paso por iteración
trace = FALSE
)
)
pso_history[[iter]] <- get(".pso_particles", envir = .GlobalEnv)
}
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
## Warning in psoptim(par = c(NA, NA), fn = rastrigin_with_history, lower =
## c(-5.12, : unknown names in control: maxfn
# Generar gráficos por iteración
dir_temp <- tempdir()
files <- character(0)
for (i in seq_along(pso_history)) {
png_filename <- file.path(dir_temp, sprintf("pso_frame_%03d.png", i))
png(png_filename, width = 600, height = 600)
plot(pso_history[[i]], xlim = c(-5.12, 5.12), ylim = c(-5.12, 5.12),
pch = 19, col = rgb(0, 0, 1, 0.5),
xlab = "x", ylab = "y", main = paste("PSO Iteración", i))
points(0, 0, pch = 8, col = "red", cex = 1.5) # Óptimo global
dev.off()
files <- c(files, png_filename)
}
# Crear animación
img_list <- image_read(files)
animation <- image_animate(img_list, fps = 10)
# Guardar GIF
output_dir <- "Trabajo1_files"
if (!dir.exists(output_dir)) dir.create(output_dir)
gifpso_path <- file.path(output_dir, "rastrigin_pso_2d.gif")
image_write(animation, gifpso_path)
knitr::include_graphics(gifpso_path)
Fig X. Animación Optimización de particulas 2D
pso_rastrigin_3d <- psoptim(
par = c(NA, NA), # Inicialización (puede ser aleatoria)
fn = rastrigin, # Función a minimizar
lower = c(-5.12, -5.12, -5.12), # Límites inferiores
upper = c(5.12, 5.12, 5.12), # Límites superiores
control = list(
maxit = 200, # Número de iteraciones
s = 40, # Tamaño del enjambre (partículas)
trace = 1 # Mostrar progreso
)
)
## S=40, K=3, p=0.07314, w0=0.7213, w1=0.7213, c.p=1.193, c.g=1.193
## v.max=NA, d=14.48, vectorize=FALSE, hybrid=off
## It 10: fitness=2.025
## It 20: fitness=0.1927
## It 30: fitness=0.1927
## It 40: fitness=0.0657
## It 50: fitness=0.00467
## It 60: fitness=0.003574
## It 70: fitness=0.0003263
## It 80: fitness=0.0003263
## It 90: fitness=7.644e-07
## It 100: fitness=7.327e-07
## It 110: fitness=1.288e-07
## It 120: fitness=1.251e-07
## It 130: fitness=2.931e-10
## It 140: fitness=1.346e-10
## It 150: fitness=1.141e-11
## It 160: fitness=9.237e-14
## It 170: fitness=9.237e-14
## It 180: fitness=1.066e-14
## It 190: fitness=7.105e-15
## It 200: fitness=0
## Maximal number of iterations reached
# Almacenar historial
rastrigin_de_history <- list()
rastrigin_with_history_de <- function(x) {
val <- rastrigin(x)
rastrigin_de_history <<- c(rastrigin_de_history, list(list(x = x, value = val)))
return(val)
}
# Ejecutar DEoptim
set.seed(123)
control_params <- DEoptim.control(NP = 50, itermax = 100, trace = FALSE)
res_de <- DEoptim(
fn = rastrigin_with_history_de,
lower = c(-5.12, -5.12),
upper = c(5.12, 5.12),
control = control_params
)
# Extraer el mejor individuo por generación
evals <- split(rastrigin_de_history, rep(1:101, each = 50)) # 1 inicial + 100 iteraciones
best_by_gen <- lapply(evals, function(gen) {
gen[[which.min(sapply(gen, function(p) p$value))]]
})
rastrigin_df_de <- data.frame(
step = 1:length(best_by_gen),
x = sapply(best_by_gen, function(p) p$x[1]),
y = sapply(best_by_gen, function(p) p$x[2]),
z = sapply(best_by_gen, function(p) p$value)
)
# Crear grid para el fondo
x_vals <- seq(-5.12, 5.12, length.out = 200)
y_vals <- seq(-5.12, 5.12, length.out = 200)
grid <- expand.grid(x = x_vals, y = y_vals) %>%
rowwise() %>%
mutate(z = rastrigin(c(x, y))) %>%
ungroup()
# Procesar historial
evals <- split(rastrigin_de_history, rep(1:101, each = 50)) # 1 inicial + 100 iteraciones
# Preparar carpeta temporal para imágenes
dir_temp <- tempdir()
files <- character(0)
# Generar una imagen por iteración
for (i in seq_along(evals)) {
gen <- evals[[i]]
coords <- t(sapply(gen, function(p) p$x))
png_filename <- file.path(dir_temp, sprintf("frame_%03d.png", i))
png(png_filename, width = 600, height = 600)
plot(coords, xlim = c(-5.12, 5.12), ylim = c(-5.12, 5.12),
main = paste("Iteración", i - 1),
xlab = "x", ylab = "y", pch = 19, col = rgb(0.2, 0.6, 0.9, 0.5))
dev.off()
files <- c(files, png_filename)
}
# Crear GIF
img_list <- image_read(files)
animation <- image_animate(img_list, fps = 10)
# Guardar GIF
output_dir <- "Trabajo1_files"
if (!dir.exists(output_dir)) dir.create(output_dir)
gifed_path <- file.path(output_dir, "rastrigin_de_animation.gif")
image_write(animation, gifed_path)
knitr::include_graphics(gifed_path)
Fig X. Animación Optimización de particulas 2D
La función de Rosenbrock de forma generalizada para para n variables se define como:
\[ f(\mathbf{x}) = \sum_{i=1}^{n-1} \left[ a - x_i \right]^2 + b \left( x_{i+1} - x_i^2 \right)^2 \] Donde, \(\mathbf{x} = (x_1, x_2, \ldots, x_n) \in \mathbb{R}^n\)
\(a\) y \(b\) son parámetros que controlan la forma de la función. Típicamente, \(a = 1\) y \(b=100\), para estos parámetros comunes el mínimo global se va encontrar en el conjunto de coordenadas \((x_1, x_2, \ldots, x_n) = (1, 1, \ldots, 1)\) donde \(f(\mathbf{x^*}) = 0\)
#Definimos la función general
rosenbrock_n <- function(x, a = 1, b = 100) {
n <- length(x)
sum_value <- 0
for(i in 1:(n-1)) {
sum_value <- sum_value + (a - x[i])^2 + b * (x[i+1] - x[i]^2)^2
}
return(sum_value)
}
Probamos los minimos globales, para distintos números de variables:
rosenbrock_n(c(1,1))
## [1] 0
rosenbrock_n(c(1,1,1))
## [1] 0
rosenbrock_n(c(1,1,1,1))
## [1] 0
Procedemos a visualizar la función para los casos de dos y tres variables, ya que con una sola variable esta se reduce a una parábola convexa simple. Esta forma trivial no representa un reto significativo para los métodos de optimización, por lo que no resulta adecuada para los fines de este análisis.
Visualización en 3d para dos variables (x1,x2)
Fig 8. Visualización de la función Rosenbrock f(x1,x2) (3D)
En la visualización 3D de la función de Rosenbrock en dos dimensiones, con variables (x1,x2), se observa un paisaje caracterizado por un valle estrecho, curvado y alargado. El mínimo global se encuentra en el punto (x1,x2)=(1,1), pero el camino hacia este mínimo no es directo.
La superficie tiene una pendiente suave que desciende hacia el fondo del valle, mientras que los bordes laterales presentan pendientes pronunciadas, formando una especie de “canal en forma de U” que se curva a lo largo del plano. Esta topografía hace que, aunque el gradiente siempre apunte hacia el mínimo, los métodos de optimización puedan desviarse, oscilar o converger lentamente si no están bien ajustados.También da la sensación de que la función es simétrica alrededor de x2=0, teniendo así dos mínimos globales, lo cual en verdad no es cierto.
Visualización en 2d del mapa de calor, vista superior para dos variables (x1,x2)
Fig 9. Mapa de Calor Función Rosenbrock
Se utiliza un punto inicial aleatorio dentro del rango [−3,3] y [3,-3,3] y a partir de allí el algoritmo busca aproximarse al mínimo global de la función, se utiliza el método BFGS (Broyden–Fletcher–Goldfarb–Shanno) ya que es un algoritmo de optimización mucho eficiente y robusto para funciones suaves y no lineales, como es el caso de la función Rosenbrock.
set.seed(1986)
x02 <- runif(2, -3, 3)
res_gradiente_2d <- optim(
par = x02,
fn = rosenbrock_n,
method = "BFGS"
)
x03 <- runif(3, -3, 3)
res_gradiente_3d <- optim(
par = x03,
fn = rosenbrock_n,
method = "BFGS"
)
En este caso se define un espacio de búsqueda continuo en el rango [−3,3] para x1,x2 y x3 y se configura una población inicial de 50 individuos. El algoritmo evoluciona durante un máximo de 100 iteraciones o hasta que no se observe mejora en 50 generaciones consecutivas.
set.seed(1986)
# Optimizamos con GA
ga_evolutivo_2d <- ga(
type = "real-valued",
fitness = function(x) -rosenbrock_n(x),
lower = c(-3, -3),
upper = c(3, 3),
popSize = 50,
maxiter = 50,
run = 50,
monitor=FALSE
)
nro_evaluaciones_ga_2d <- ga_evolutivo_2d@popSize * ga_evolutivo_2d@iter
ga_evolutivo_3d <- ga(
type = "real-valued",
fitness = function(x) -rosenbrock_n(x),
lower = c(-3, -3, -3),
upper = c(3, 3, 3),
popSize = 50,
maxiter = 50,
run=50,
monitor=FALSE
)
nro_evaluaciones_ga_3d <- ga_evolutivo_3d@popSize * ga_evolutivo_3d@iter
De nuevo comenzamos con una posición aleatoria en el rango [−3,3] para cada una de las variables variables y se realiza hasta 100 iteraciones para encontrar el mínimo.
#2D
evaluaciones_pso <- 0
rosenbrock_n_evaluaciones_pso <- function(x) {
evaluaciones_pso <<- evaluaciones_pso + 1
rosenbrock_n(x)
}
# Ejecutar PSO con función evaluaciones_psoa
set.seed(1986)
res_pso_2d <- psoptim(
par = runif(2, -3, 3),
fn = rosenbrock_n_evaluaciones_pso,
lower = c(-3, -3),
upper = c(3, 3),
control = list(maxit = 100)
)
#3D
evaluaciones_pso_3d <- 0
rosenbrock_n_evaluaciones_pso_3d <- function(x) {
evaluaciones_pso_3d <<- evaluaciones_pso_3d + 1
rosenbrock_n(x)
}
res_pso_3d <- psoptim(
par = runif(3, -3, 3),
fn = rosenbrock_n_evaluaciones_pso_3d,
lower = c(-3, -3, -3),
upper = c(3, 3, 3),
control = list(maxit = 100)
)
En este caso establecemos una población inicial de 50 individuos (NP = 50) y se permiten hasta 100 iteraciones (itermax = 100) para la optimización. En este caso también se busca el mínimo de la función dentro del rango [−3,3] para todas las variables.
# Optimizamos con DE
set.seed(1986)
control_params <- DEoptim.control(NP = 50, itermax = 70,trace=FALSE)
res_de_2d <- DEoptim(
fn = rosenbrock_n,
lower = c(-3, -3),
upper = c(3, 3),
control = control_params
)
evaluaciones_de <- control_params$NP * res_de_2d$optim$iter
res_de_3d <- DEoptim(
fn = rosenbrock_n,
lower = c(-3, -3, -3),
upper = c(3, 3, 3),
control = control_params
)
evaluaciones_de_3d <- control_params$NP * res_de_2d$optim$iter
A continuación observamos los resultados obtenidos, de la optimización de la función de Rosenbrock para dos variables, con los cuatro métodos.
| Método | Valor de x1 | Valor de x2 | Valor Función Objetivo | Número de Evaluaciones |
|---|---|---|---|---|
| Descenso por gradiente | 0.9998331 | 0.9996662 | 0.00e+00 | 53 |
| Algoritmos evolutivos | 0.9856456 | 0.9707295 | -2.65e-04 | 2500 |
| Optimización de partículas | 1.0037167 | 1.0074443 | 1.38e-05 | 1200 |
| Evolución Diferencial | 1.0000652 | 1.0001343 | 0.00e+00 | 3500 |
Para dos dimensiones los resultados nos muestran que todos los métodos lograron aproximarse al mínimo global de la función de Rosenbrock, aunque con diferencias significativas en eficiencia y precisión. El método de descenso por gradiente (BFGS) fue el más eficiente, alcanzando un valor prácticamente óptimo con solo 53 evaluaciones. En contraste, los métodos heurísticos requirieron muchas más evaluaciones: evolución diferencial logró un valor casi perfecto con 3500 evaluaciones, mientras que PSO alcanzó un resultado muy cercano al óptimo con 1200 evaluaciones. Por su parte, el algoritmo evolutivo fue el menos preciso, con un valor ligeramente más alejado del óptimo y un costo computacional alto (2500 evaluaciones). En resumen, los métodos tradicionales como el gradiente descendente son más eficientes cuando la función es suave y bien comportada, mientras que los métodos heurísticos ofrecen robustez frente a funciones complejas, aunque con mayor costo computacional. Es importante recordar que en el caso de métodos basados en gradiente as condiciones iniciales juegan un papel crucial en la optimización, ya que pueden determinar si el algoritmo converge rápidamente o queda atrapado en regiones poco prometedoras.
| Método | Valor de x1* | Valor de x2* | Valor de x3* | Valor Óptimo de la Función Objetivo | Número de Evaluaciones |
|---|---|---|---|---|---|
| Descenso por gradiente | 0.9998799 | 0.9997602 | 0.9995203 | 0.0000001 | 143 |
| Algoritmos evolutivos | 0.5741961 | 0.3335581 | 0.1089820 | -0.6274608 | 2500 |
| Optimización de partículas | 0.7731997 | 0.5999325 | 0.5999325 | 0.2178242 | 1300 |
| Evolución Diferencial | 1.0347233 | 1.0661181 | 1.0661181 | 0.0110632 | 3500 |
El descenso por gradiente (BFGS) es el método más eficiente, logrando el valor óptimo con solo 143 evaluaciones. Los algoritmos evolutivos requieren 2500 evaluaciones, pero terminan con un valor de función objetivo significativamente más alejado del mínimo. La optimización por partículas (PSO) logra un valor positivo con 1300 evaluaciones, pero no se aproxima tanto al óptimo como el descenso por gradiente. Finalmente, la evolución diferencial con 3500 evaluaciones también se queda cerca del óptimo, pero no supera al gradiente. En general, los métodos heurísticos necesitan más evaluaciones y no alcanzan la precisión del descenso por gradiente.
knitr::include_graphics("Trabajo1_files/rosenbrock_gd_2d.gif")
Fig X. Animación Descenso del gradiente 2D
knitr::include_graphics("Trabajo1_files/rosenbrock_de_2d.gif")
Fig X. Animación Evolución Diferencial 2D
knitr::include_graphics("Trabajo1_files/rosenbrock_ga_2d.gif")
Fig X. Animación Algoritmos Evolutivos 2D
knitr::include_graphics("Trabajo1_files/rosenbrock_pso_2d.gif")
Fig X. Animación Optimización PSO 2D
\(CT_{ij} = PrecioPeajes_{ij} + Distancia_{ij}(km) * PrecioGasolina/km + Salario(Horas) * Tiempo_{ij}(Horas)\)
ciudades = c(
"Bogotá",
"Medellín",
"Cali",
"Barranquilla",
"Cartagena",
"Bucaramanga",
"Cúcuta",
"Pereira",
"Manizales",
"Ibagué",
"Santa Marta",
"Villavicencio",
"Neiva"
)
matriz_distancia <- read.csv("matriz_distancia.csv", row.names = 1)
matriz_tiempos <- read.csv("matriz_tiempos.csv", row.names = 1)
matriz_peajes <- read.csv("matriz_peajes.csv", row.names = 1)
colnames(matriz_distancia) <- ciudades
colnames(matriz_tiempos) <- ciudades
colnames(matriz_peajes) <- ciudades
rownames(matriz_distancia) <- ciudades
rownames(matriz_tiempos) <- ciudades
rownames(matriz_peajes) <- ciudades
matriz_costo_gasolina <- 191*matriz_distancia
matriz_costo_salario <- 5416*matriz_tiempos
matriz_costo_total <- matriz_peajes + matriz_costo_gasolina + matriz_costo_salario
matriz_costo_total <- as.matrix(matriz_costo_total)
# Fijar semilla para reproducibilidad
set.seed(42)
# Definición de parámetros
cities_count <- 13 # Número total de ciudades
eroso_influence <- 1 # Influencia de la feromona (antes gamma)
visibility_influence <- 2 # Influencia de la visibilidad (antes beta)
evap_rate <- 0.5 # Tasa de evaporación de feromonas
pheromone_strength <- 100 # Constante para depósito de feromonas
ants_count <- cities_count # Una hormiga por estado
cities_names <- as.list(rownames(matriz_costo_total))
# Inicialización de matrices
tau_matrix <- matrix(1, nrow = cities_count, ncol = cities_count) # Feromonas
eta_matrix <- 1 / matriz_costo_total # Visibilidad
eta_matrix[!is.finite(eta_matrix)] <- 0 # Evitar Inf/NaN
# Variables para la mejor solución
best_path <- integer(0)
best_path_cost <- Inf
best_iteration <- NA
# Bucle principal: 100 iteraciones
for (iter_idx in seq_len(100)) {
all_paths <- vector("list", ants_count)
all_costs <- numeric(ants_count)
for (ant_idx in seq_len(ants_count)) {
current_node <- sample(seq_len(cities_count), 1)
visited_nodes <- current_node
path_cost <- 0
# Construcción de la ruta
for (step_idx in seq_len(cities_count - 1)) {
candidates <- setdiff(seq_len(cities_count), visited_nodes)
trans_probs <- (
tau_matrix[current_node, candidates]^eroso_influence *
eta_matrix[current_node, candidates]^visibility_influence
)
if (all(trans_probs == 0)) trans_probs <- rep(1, length(candidates))
if (length(candidates) == 1) {
next_node <- candidates
} else {
next_node <- sample(candidates, 1, prob = trans_probs)
}
path_cost <- path_cost + matriz_costo_total[current_node, next_node]
current_node <- next_node
visited_nodes <- c(visited_nodes, current_node)
}
# Regreso al inicio
dir_cost <- matriz_costo_total[current_node, visited_nodes[1]]
path_cost <- path_cost + dir_cost
all_paths[[ant_idx]] <- visited_nodes
all_costs[ant_idx] <- path_cost
# Actualizar mejor ruta
if (path_cost < best_path_cost) {
best_path_cost <- path_cost
best_path <- visited_nodes
best_iteration <- iter_idx
}
}
# Mostrar el costo mínimo encontrado en esta iteración
cat(sprintf("Iteración %3d | Costo mínimo: $%0.2f\n", iter_idx, min(all_costs)))
# Evaporación de feromonas
tau_matrix <- tau_matrix * (1 - evap_rate)
# Depósito de feromonas
for (i in seq_along(all_paths)) {
path_nodes <- all_paths[[i]]
cost_val <- all_costs[i]
for (j in seq_len(length(path_nodes) - 1)) {
tau_matrix[path_nodes[j], path_nodes[j+1]] <-
tau_matrix[path_nodes[j], path_nodes[j+1]] + pheromone_strength / cost_val
}
# Feromona por retorno al inicio
tau_matrix[path_nodes[length(path_nodes)], path_nodes[1]] <-
tau_matrix[path_nodes[length(path_nodes)], path_nodes[1]] + pheromone_strength / cost_val
}
}
## Iteración 1 | Costo mínimo: $1854184.46
## Iteración 2 | Costo mínimo: $2033631.31
## Iteración 3 | Costo mínimo: $1989883.88
## Iteración 4 | Costo mínimo: $1900415.42
## Iteración 5 | Costo mínimo: $2004254.67
## Iteración 6 | Costo mínimo: $1935468.36
## Iteración 7 | Costo mínimo: $1880289.30
## Iteración 8 | Costo mínimo: $1991563.49
## Iteración 9 | Costo mínimo: $1866308.66
## Iteración 10 | Costo mínimo: $2109305.43
## Iteración 11 | Costo mínimo: $2009514.33
## Iteración 12 | Costo mínimo: $1999434.72
## Iteración 13 | Costo mínimo: $1740373.28
## Iteración 14 | Costo mínimo: $1988546.19
## Iteración 15 | Costo mínimo: $1781455.97
## Iteración 16 | Costo mínimo: $1962146.34
## Iteración 17 | Costo mínimo: $1782676.19
## Iteración 18 | Costo mínimo: $1705238.57
## Iteración 19 | Costo mínimo: $1880916.28
## Iteración 20 | Costo mínimo: $1677252.07
## Iteración 21 | Costo mínimo: $1677252.07
## Iteración 22 | Costo mínimo: $1719466.68
## Iteración 23 | Costo mínimo: $1654270.71
## Iteración 24 | Costo mínimo: $1824424.38
## Iteración 25 | Costo mínimo: $1756562.43
## Iteración 26 | Costo mínimo: $1677252.07
## Iteración 27 | Costo mínimo: $1691661.19
## Iteración 28 | Costo mínimo: $1677252.07
## Iteración 29 | Costo mínimo: $1677252.07
## Iteración 30 | Costo mínimo: $1677252.07
## Iteración 31 | Costo mínimo: $1677252.07
## Iteración 32 | Costo mínimo: $1677252.07
## Iteración 33 | Costo mínimo: $1691661.19
## Iteración 34 | Costo mínimo: $1677252.07
## Iteración 35 | Costo mínimo: $1724350.33
## Iteración 36 | Costo mínimo: $1677252.07
## Iteración 37 | Costo mínimo: $1654270.71
## Iteración 38 | Costo mínimo: $1677252.07
## Iteración 39 | Costo mínimo: $1677252.07
## Iteración 40 | Costo mínimo: $1677252.07
## Iteración 41 | Costo mínimo: $1654270.71
## Iteración 42 | Costo mínimo: $1691661.19
## Iteración 43 | Costo mínimo: $1691661.19
## Iteración 44 | Costo mínimo: $1691661.19
## Iteración 45 | Costo mínimo: $1740740.51
## Iteración 46 | Costo mínimo: $1691661.19
## Iteración 47 | Costo mínimo: $1691661.19
## Iteración 48 | Costo mínimo: $1691661.19
## Iteración 49 | Costo mínimo: $1691661.19
## Iteración 50 | Costo mínimo: $1691661.19
## Iteración 51 | Costo mínimo: $1691661.19
## Iteración 52 | Costo mínimo: $1677252.07
## Iteración 53 | Costo mínimo: $1691661.19
## Iteración 54 | Costo mínimo: $1691661.19
## Iteración 55 | Costo mínimo: $1691661.19
## Iteración 56 | Costo mínimo: $1691661.19
## Iteración 57 | Costo mínimo: $1691661.19
## Iteración 58 | Costo mínimo: $1691661.19
## Iteración 59 | Costo mínimo: $1691661.19
## Iteración 60 | Costo mínimo: $1691661.19
## Iteración 61 | Costo mínimo: $1691661.19
## Iteración 62 | Costo mínimo: $1691661.19
## Iteración 63 | Costo mínimo: $1691661.19
## Iteración 64 | Costo mínimo: $1691661.19
## Iteración 65 | Costo mínimo: $1691661.19
## Iteración 66 | Costo mínimo: $1691661.19
## Iteración 67 | Costo mínimo: $1691661.19
## Iteración 68 | Costo mínimo: $1691661.19
## Iteración 69 | Costo mínimo: $1691661.19
## Iteración 70 | Costo mínimo: $1691661.19
## Iteración 71 | Costo mínimo: $1691661.19
## Iteración 72 | Costo mínimo: $1691661.19
## Iteración 73 | Costo mínimo: $1691661.19
## Iteración 74 | Costo mínimo: $1691661.19
## Iteración 75 | Costo mínimo: $1691661.19
## Iteración 76 | Costo mínimo: $1691661.19
## Iteración 77 | Costo mínimo: $1691661.19
## Iteración 78 | Costo mínimo: $1691661.19
## Iteración 79 | Costo mínimo: $1691661.19
## Iteración 80 | Costo mínimo: $1691661.19
## Iteración 81 | Costo mínimo: $1691661.19
## Iteración 82 | Costo mínimo: $1691661.19
## Iteración 83 | Costo mínimo: $1691661.19
## Iteración 84 | Costo mínimo: $1691661.19
## Iteración 85 | Costo mínimo: $1691661.19
## Iteración 86 | Costo mínimo: $1691661.19
## Iteración 87 | Costo mínimo: $1691661.19
## Iteración 88 | Costo mínimo: $1691661.19
## Iteración 89 | Costo mínimo: $1691661.19
## Iteración 90 | Costo mínimo: $1691661.19
## Iteración 91 | Costo mínimo: $1691661.19
## Iteración 92 | Costo mínimo: $1691661.19
## Iteración 93 | Costo mínimo: $1691661.19
## Iteración 94 | Costo mínimo: $1691661.19
## Iteración 95 | Costo mínimo: $1691661.19
## Iteración 96 | Costo mínimo: $1691661.19
## Iteración 97 | Costo mínimo: $1691661.19
## Iteración 98 | Costo mínimo: $1691661.19
## Iteración 99 | Costo mínimo: $1691661.19
## Iteración 100 | Costo mínimo: $1691661.19
# Convertir mejor ruta a nombres y agregar retorno al inicio
best_path_names <- cities_names[best_path]
best_path_names <- c(best_path_names, best_path_names[1])
# Impresión de resultados
cat("\nMejor ruta encontrada:\n")
##
## Mejor ruta encontrada:
cat(paste(best_path_names, collapse = " → "), "\n")
## Cúcuta → Bucaramanga → Santa Marta → Barranquilla → Cartagena → Medellín → Manizales → Ibagué → Pereira → Cali → Neiva → Bogotá → Villavicencio → Cúcuta
cat("\nDetalle de costos por tramo:\n")
##
## Detalle de costos por tramo:
cat(sprintf("%-30s %-30s %10s\n", "Origen", "Destino", "Costo"))
## Origen Destino Costo
cat(strrep("-", 70), "\n")
## ----------------------------------------------------------------------
total_cost <- 0
for (idx in seq_along(best_path)) {
origin <- best_path[idx]
dest <- if (idx == length(best_path)) best_path[1] else best_path[idx+1]
seg_cost <- matriz_costo_total[origin, dest]
total_cost <- total_cost + seg_cost
cat(sprintf("%-30s %-30s $%9.2f\n",
cities_names[[origin]], cities_names[[dest]], seg_cost))
}
## Cúcuta Bucaramanga $ 73397.27
## Bucaramanga Santa Marta $208392.43
## Santa Marta Barranquilla $ 46121.28
## Barranquilla Cartagena $ 56307.19
## Cartagena Medellín $298235.20
## Medellín Manizales $117686.23
## Manizales Ibagué $ 89019.42
## Ibagué Pereira $ 59348.34
## Pereira Cali $111118.18
## Cali Neiva $120726.62
## Neiva Bogotá $152503.69
## Bogotá Villavicencio $ 88845.24
## Villavicencio Cúcuta $232569.62
cat("\nEstadísticas:\n")
##
## Estadísticas:
cat(sprintf("Ciudades (incluye retorno): %d\n", length(best_path_names)))
## Ciudades (incluye retorno): 14
cat(sprintf("Costo total: $%0.2f\n", total_cost))
## Costo total: $1654270.71
cat(sprintf("Costo promedio por tramo: $%0.2f\n", total_cost / length(best_path)))
## Costo promedio por tramo: $127251.59
cat(sprintf("Mejor costo encontrado en la iteración: %d\n", best_iteration))
## Mejor costo encontrado en la iteración: 41
# Data.frame de ciudades con coordenadas
coords_df <- data.frame(
city = c("Bogotá", "Medellín", "Cali", "Barranquilla", "Cartagena",
"Bucaramanga", "Cúcuta", "Pereira", "Manizales", "Ibagué",
"Santa Marta", "Villavicencio", "Neiva"),
lon = c(-74.0721, -75.5812, -76.5320, -74.7813, -75.4794,
-73.1227, -72.4965, -75.6961, -75.5174, -75.2322,
-74.1990, -73.6260, -75.2803),
lat = c(4.7110, 6.2442, 3.4516, 10.9685, 10.3910,
7.1193, 7.8891, 4.8133, 5.0672, 4.4389,
11.2408, 4.1420, 2.9273),
stringsAsFactors = FALSE
)
# Para comprobar
print(coords_df)
## city lon lat
## 1 Bogotá -74.0721 4.7110
## 2 Medellín -75.5812 6.2442
## 3 Cali -76.5320 3.4516
## 4 Barranquilla -74.7813 10.9685
## 5 Cartagena -75.4794 10.3910
## 6 Bucaramanga -73.1227 7.1193
## 7 Cúcuta -72.4965 7.8891
## 8 Pereira -75.6961 4.8133
## 9 Manizales -75.5174 5.0672
## 10 Ibagué -75.2322 4.4389
## 11 Santa Marta -74.1990 11.2408
## 12 Villavicencio -73.6260 4.1420
## 13 Neiva -75.2803 2.9273
library(osrm)
library(sf)
library(leaflet)
library(htmltools)
# Función de petición con reintentos
get_route <- function(src, dst, retries = 3, pause = 1) {
for (i in seq_len(retries)) {
attempt <- try(
osrmRoute(src = src, dst = dst, overview = "full"),
silent = TRUE
)
if (!inherits(attempt, "try-error")) return(attempt)
Sys.sleep(pause)
}
sf::st_linestring(matrix(c(src[1], dst[1], src[2], dst[2]),
ncol = 2, byrow = TRUE)) %>%
sf::st_sfc(crs = 4326) %>%
sf::st_sf()
}
# Ordenar coordenadas según la ruta
route_coords <- coords_df[match(best_path_names, coords_df$city), ]
# Quitar la última ciudad (repetida para cerrar el ciclo)
route_coords_labeled <- route_coords[-nrow(route_coords), ]
route_coords_labeled$order <- seq_len(nrow(route_coords_labeled)) # Orden de visita
# Obtener los segmentos de la ruta (incluye el regreso al origen)
route_segments <- lapply(seq_len(nrow(route_coords) - 1), function(i) {
src <- as.numeric(route_coords[i, c("lon", "lat")])
dst <- as.numeric(route_coords[i+1, c("lon", "lat")])
get_route(src, dst)
})
# Unir todos los segmentos en un solo objeto sf
routes_sf <- do.call(rbind, route_segments)
# Crear el mapa
leaflet() %>%
addTiles() %>%
addPolylines(data = routes_sf,
color = "darkred",
weight = 4,
opacity = 0.8) %>%
addCircleMarkers(data = route_coords,
~lon, ~lat,
radius = 5,
color = "white",
fillColor = "blue",
fillOpacity = 0.9,
label = ~city) %>%
addLabelOnlyMarkers(data = route_coords_labeled,
~lon, ~lat,
label = ~as.character(order),
labelOptions = labelOptions(noHide = TRUE,
direction = 'top',
textOnly = TRUE,
style = list(
"color" = "black",
"font-weight" = "bold",
"font-size" = "12px"
))) %>%
setView(lng = mean(route_coords$lon),
lat = mean(route_coords$lat),
zoom = 6)
library(leaflet)
library(htmltools)
# 1. Obtener coordenadas ordenadas por la ruta
route_coords <- coords_df[match(best_path_names, coords_df$city), ]
# 2. Crear una versión sin la última ciudad (que es repetición de la primera)
route_coords_labeled <- route_coords[-nrow(route_coords), ]
route_coords_labeled$order <- seq_len(nrow(route_coords_labeled))
# 3. Crear el mapa con líneas, marcadores y etiquetas de orden
leaflet(route_coords) %>%
addTiles() %>%
addPolylines(~lon, ~lat, color = "red", weight = 3, opacity = 0.7,
label = best_path_names) %>%
addCircleMarkers(~lon, ~lat, radius = 5,
color = "blue", fill = TRUE,
label = ~city) %>%
addLabelOnlyMarkers(data = route_coords_labeled,
~lon, ~lat,
label = ~as.character(order),
labelOptions = labelOptions(noHide = TRUE,
direction = 'top',
textOnly = TRUE,
style = list(
"color" = "black",
"font-weight" = "bold",
"font-size" = "12px"
)))
Natalia Remolina: análisis de resultados descenso del gradiente en Rastrigin, ejecución del Web Scraping, optimización combinatoria colonia de hormigas (algoritmo y gráficos).
Jonatan Sanchez: creación del repositorio GitHub, optimización por descenso del gradiente para Rastrigin (función, gráficos y animaciones) y análisis de resultados, ejecución del Web Scraping, optimización combinatoria colonia de hormigas (algoritmo y gráficos).
Carlos Calle: creación del repositorio GitHub, creación y ejecución del Web Scraping para obtener información de distancia, tiempo y costo de peajes desde el aplicativo Hermes de Invias, definición de los optimizadores para Rastrigin en algoritmos evolutivos y optimización de párticulas, optimización combinatoria colonia de hormigas (algoritmo y gráficos).
Juan Camilo Valencia: optimización combinatoria colonia de hormigas (algoritmo y gráficos). Graficos de algortimos evolutivos, optimización de particulas y evolución diferencial.
David Castro: Optimización de la Función Rosenbrock, en 2d,3d con Descenso por gradiente y Metodos heuristicos, graficas.