De la heurística al control óptimo: 3 perros, 200 ovejas

Publicado el

heurística «empujar y girar»
Tres perros dando vueltas alrededor de un rebaño de doscientas ovejas que sigue lejos del objetivo.
control óptimo (CasADi + IPOPT)
El rebaño de doscientas ovejas ya sobre el objetivo, con los tres perros parados lejos.
La misma tarea, la misma dinámica y la misma cota: 3 perros conducen 200 ovejas al objetivo en 400 unidades de tiempo.

La heurística del artículo anterior —colocarse detrás del rebaño y empujar— llevaba 30 ovejas al objetivo. Con 200 ya no llega. Esto es el mismo problema escrito como un problema de control óptimo: en vez de decidir qué hace el perro en cada instante, se escribe lo que se quiere conseguir y lo encuentra un optimizador. Con CasADi, IPOPT y dos horas de CPU.

Lectura rápida

  • De dónde viene. Es la continuación de Guiar un rebaño con un perro que solo repele, donde la política «empujar y girar» del TFM se montó en LAMMPS y acababa en un ciclo límite: llegaba, pero no se paraba. Aquí cambia la escala —200 ovejas y 3 perros— y cambia el enfoque.
  • Por qué hace falta. Con 200 ovejas la heurística se queda a 86,9 del objetivo al acabar el horizonte, después de gastar 428 300 de esfuerzo dando vueltas. No es un fallo de la implementación: es la limitación que el propio TFM le atribuye.
  • El planteamiento. Estado de 812 números, control las aceleraciones de los tres perros acotadas a por componente, y un coste con cuatro términos normalizados —llegar, no dispersar, esfuerzo y seguimiento— de modo que signifique literalmente «agrupar pesa una quinta parte de llegar».
  • Cómo se resuelve. Disparo simple: las incógnitas son solo los 960 controles y el estado sale de integrar con RK4. El gradiente, por diferenciación automática inversa de CasADi; la minimización, IPOPT con hessiano L-BFGS.
  • Qué sale. Distancia final 2,0 frente a 86,9; llega a menos de 20 en t = 60, la heurística nunca; esfuerzo 39 982 frente a 428 300. El óptimo no gana por poco: llega, y gastando diez veces menos.
  • Lo que no es gratis. 105 minutos de IPOPT para no converger: para en el máximo de iteraciones con la condición KKT en 3,6·10⁻⁴. Es el mejor punto encontrado, no un óptimo certificado.
  • La lección transferible. El optimizador no hace lo que uno quiere, hace lo que uno escribe: pedir compacidad solo al final le autoriza a estirar el rebaño en un arco a mitad de camino, y con 20 ovejas lo hace.
  • Se puede repetir. Un run.sh, Python con CasADi y una escalera de comprobaciones que se ejecuta en cinco minutos.

Por qué la heurística deja de funcionar

La política del artículo anterior cabe en dos frases: un término radial coloca al perro a una distancia fija del centro del rebaño y un término tangencial lo hace girar hasta ponerse detrás, en la línea que une rebaño y objetivo. Con 30 ovejas funciona: el perro empuja, el rebaño avanza en diagonal y llega.

Con 200 ovejas el rebaño ocupa mucho más sitio. Los perros tienen que colocarse más lejos —aquí, al radio del rebaño inicial más 25—, y desde más lejos cada corrección de ángulo es una vuelta más larga. El resultado está en el panel izquierdo de la animación: los tres perros se pasan el horizonte orbitando alrededor del rebaño, que avanza a regañadientes.

En 400 unidades de tiempo, con la heurística
Distancia inicial del rebaño al objetivo202,4
Distancia al final del horizonte86,9
Cuándo baja de 20nunca
Esfuerzo 428 300

Conviene decir lo que esto no es: no es que la política se rompa ni que el rebaño se disperse —la dispersión se queda en 524, por debajo de la de partida—. Es que no da tiempo. Donde con 30 ovejas recorría 202 unidades en 400 de tiempo, con 200 recorre 115.

Y es exactamente lo que el TFM anticipaba de su propia heurística: que «tiene limitaciones cuando la dispersión de las ovejas es grande con respecto a la distancia entre la media de las posiciones de las ovejas y la posición del perro». Cuando el rebaño es grande y el perro está lejos, girar sale caro.

Detalle técnicoTres perros con una política escrita para uno

La heurística del TFM es para un solo perro, y los tres tendrían la misma consigna: se amontonarían detrás del rebaño, en la misma línea, empujando los tres en el mismo sitio. Se reparte el arco dando a cada perro un desfase respecto a la línea rebaño → objetivo, con rad. El resto es la fórmula del TFM, incluido el apagado continuo del giro cerca del objetivo —el factor — que ya hacía falta en la versión de LAMMPS.

Dos detalles más de la comparación, por si parecen trampas y no lo son:

La heurística se simula en lazo cerrado, no congelada. Es una ley de realimentación en tiempo continuo: congelarla durante un intervalo de control entero —2,5 unidades, doce veces la escala de tiempo del perro— la vuelve inestable y la dejaría peor de lo que es. Se evalúa en cada subpaso de RK4. Lo que se compara con el control óptimo es su acción media por intervalo; el esfuerzo, en cambio, se acumula subpaso a subpaso, porque promediar antes de elevar al cuadrado le rebajaría la factura.

Las dos políticas juegan con las mismas cartas. El óptimo está acotado a por componente. La heurística sin cota llega a pedir aceleraciones de 430 —su término de giro crece con la distancia al rebaño—, pero en una corrida de 30 ovejas y un perro su mediana es 5,5 y su percentil 90, 52. Con la heurística hace prácticamente lo mismo que sin cota (con un perro y 30 ovejas llega en t = 495 en vez de t = 435), así que el óptimo no gana por tener más autoridad.

El problema de control óptimo

El modelo es el mismo del artículo anterior y no se ha tocado ni un parámetro: puntos de masa unidad en el plano, fuerzas radiales y rozamiento, con la repulsión del perro sobre la oveja asimétrica —la oveja huye, el perro no siente la reacción—. Con ovejas y perros:

con , y . Las son el control. El estado son las posiciones y velocidades de todos: con 200 ovejas y 3 perros, números.

El coste

Cuatro términos, todos normalizados para que los pesos signifiquen algo:

sujeto a , donde es el centro del rebaño, su distancia inicial al objetivo y el número de intervalos de control. Cada término vale para «no he hecho nada» y para «perfecto», así que quiere decir literalmente que agrupar pesa una quinta parte de llegar. Pesos usados: , , .

Los dos primeros términos y el esfuerzo son los del TFM. El seguimiento no estaba en el enunciado, y conviene justificarlo con números en vez de con intuición: el gradiente del coste terminal respecto a los primeros controles atraviesa 160 intervalos de dinámica disipativa y llega aplastado. Lo que compra es que los perros empujen desde el principio en vez de dejarlo todo para el final. Pero no es imprescindible: con el optimizador también lleva las 200 ovejas al objetivo, solo que gastando el triple (esfuerzo 120 377 frente a 39 982) y quedándose algo más lejos (3,1 frente a 2,0).

Una regularización que no es cosmética

Un optimizador basado en gradiente necesita que todo sea derivable, y en el modelo tal cual hay dos sitios donde no lo es: revienta en y la norma no es derivable en el origen. Las dos se arreglan de una vez evaluando las fuerzas en

tanto en el como en el vector unitario . La fuerza resultante es analítica en todas partes y tiende a cero linealmente cuando dos individuos se superponen, en vez de diverger. El precio es un error relativo de : con la distancia típica entre ovejas, unas 10 unidades, un 0,5 %. Lo que eso supone en una trayectoria entera está medido contra LAMMPS más abajo, y son 5 diezmilésimas del recorrido.

Cómo se resuelve, y cuánto cuesta

Disparo simple. Las incógnitas son solo los controles; el estado sale de integrar. No hay restricciones de dinámica —se cumplen por construcción— y el problema queda como una minimización con cotas de caja, que es lo que mejor le sienta a IPOPT con hessiano L-BFGS.

horizonte
intervalos de control160 (), control constante a trozos
integraciónRK4, 10 subpasos por intervalo ()
incógnitas
gradienteadjunto (AD inversa) de CasADi
optimizadorIPOPT, hessian_approximation = limited-memory

La dinámica está vectorizada: las 200 × 200 distancias entre ovejas son dos repmat y un mtimes, no un bucle de Python. Y el bucle temporal se cierra con mapaccum, que repite el cuerpo del intervalo sin desplegar el grafo 160 veces: construirlo entero cuesta medio segundo y toda la sesión —coste, gradiente e IPOPT— cabe en 200 MB.

Lo que cuesta
construir el grafo (812 estados, 160 intervalos)0,5 s
una evaluación del coste2,5 s
un gradiente completo por AD inversa23 s
200 iteraciones de IPOPT (1093 costes + 201 gradientes)105 min
memoria200 MB
infactibilidad dual al parar3,6·10⁻⁴

Ese gradiente de 23 segundos es la razón de ser de la diferenciación automática inversa. Por diferencias finitas harían falta 961 evaluaciones del coste —más de 40 minutos— para el mismo vector, y encima aproximado. El coste de la AD inversa, en cambio, no depende del número de incógnitas: es un múltiplo pequeño del coste de evaluar una vez.

Detalle técnicoEl código: la dinámica vectorizada, el bucle con mapaccum y la llamada a IPOPT

Las fuerzas, sin un solo bucle sobre individuos. A es 2 × n, B es 2 × m, y g devuelve el módulo con signo de la fuerza (negativo = repulsión). Lo que sale es la suma de las fuerzas que los de B hacen sobre los de A, en 2 × n:

def _radial(A, B, g, eps, quita_diagonal=False):
    n, m = A.shape[1], B.shape[1]
    dx = ca.repmat(B[0, :], n, 1) - ca.repmat(A[0, :].T, 1, m)   # [i,k] = b_k - a_i
    dy = ca.repmat(B[1, :], n, 1) - ca.repmat(A[1, :].T, 1, m)
    r = ca.sqrt(dx * dx + dy * dy + eps * eps)     # la regularizacion, aqui
    c = g(r) / r                                   # g(r) * d/r = (g(r)/r) * d
    if quita_diagonal:
        c = c * (ca.DM.ones(n, m) - ca.DM.eye(n))
    uno = ca.DM.ones(m, 1)
    return ca.horzcat(ca.mtimes(c * dx, uno), ca.mtimes(c * dy, uno)).T

La suma sobre vecinos es un producto por un vector de unos: para CasADi es una operación matricial de 40 000 elementos, no 40 000 nodos en el grafo. Con la misma función salen las tres interacciones, y la asimetría del modelo es simplemente que F_op no aparece en la ecuación de los perros:

F_oo = _radial(X, X, g_oo, p.eps, quita_diagonal=True)
F_op = _radial(X, Y, g_op, p.eps)
F_pp = _radial(Y, Y, g_pp, p.eps, quita_diagonal=True)

dV = -p.nu_o * V + F_oo + F_op          # ovejas: cohesion + repulsion del perro
dW = -p.nu_p * W + F_pp + a             # perros: no sienten al rebano

El integrador y el bucle. Un intervalo de control son m subpasos de RK4 con la acción congelada, desenrollados dentro del cuerpo porque son pocos. El bucle sobre los 160 intervalos va fuera, con mapaccum, que no desenrolla:

def func_intervalo(p, h, m):
    z = ca.MX.sym("z", p.n_est)
    a = ca.MX.sym("a", 2 * p.n_p)
    zk = z
    for _ in range(m):
        zk = paso_rk4(zk, a, h, p)
    return ca.Function("intervalo", [z, a], [zk], ["z", "a"], ["zn"])

def trayectoria(paso, z0, A):            # A es 2*n_p x N
    return paso.mapaccum("rollout", A.shape[1])(z=z0, a=A)["zn"]

Sin mapaccum el grafo tendría 160 × 10 × 4 evaluaciones del campo escritas una detrás de otra. Con él, el cuerpo está una vez y el recorrido lo hace la máquina virtual de MX. (La compilación JIT a C, que para grafos pequeños vuela, aquí no terminó en diez minutos: se usa la máquina virtual.)

El coste y el NLP. Con la trayectoria ya montada, el coste son cuatro líneas y el gradiente, una:

Z = trayectoria(paso, ca.DM(z0), A)              # n_est x N
CM = ca.mtimes(S, Z[: 2 * p.n_o, :])             # centro del rebano, 2 x N
XT = ca.reshape(Z[: 2 * p.n_o, -1], 2, p.n_o)    # posiciones finales

llegar   = ca.sumsqr(media(XT) - obj) / L2
agrupar  = dispersion(XT) / D0
esfuerzo = ca.sumsqr(A) / (n * p.n_p * a_max ** 2)
seguir   = ca.sumsqr(CM - ca.repmat(obj, 1, n)) / (n * L2)

J = llegar + lam * agrupar + bet * esfuerzo + gam * seguir
gradJ = ca.Function("gradJ", [A], [ca.gradient(J, A)])

Y la llamada al solver, con las dos opciones que de verdad importan —L-BFGS, porque un hessiano de 960 × 960 por AD no es asumible, y el escalado apagado, porque lo hacemos a mano—:

nlp = {"x": ca.vec(u), "f": escala * J(u * a_max)}
solver = ca.nlpsol("ocp", "ipopt", nlp, {"ipopt": {
    "max_iter": 200, "tol": 1e-8,
    "hessian_approximation": "limited-memory",
    "limited_memory_max_history": 20,
    "nlp_scaling_method": "none",
    "mu_strategy": "adaptive",
}})
sol = solver(x0=ca.DM(A0.reshape(-1, 1, order="F") / a_max), lbx=-1.0, ubx=1.0)

El escalado no es cosmético. Tal cual, con controles del orden de 25, y IPOPT agota las iteraciones sin moverse del sitio. Hacen falta dos cosas, y las dos: que las incógnitas sean , y multiplicar el objetivo por un factor que deje el gradiente inicial en . Ese factor es la longitud del primer paso de L-BFGS, que arranca con el hessiano identidad. Con un factor 50 veces mayor, el primer paso saca los controles del rango útil y la solución cae en la cuenca equivocada: los perros se alejan hasta que su repulsión es despreciable. Un punto estacionario impecable y un resultado inútil.

La escalera de pruebas

Nada de lo de abajo se mira hasta que lo de arriba funciona. Es la misma disciplina del artículo anterior, y aquí hace más falta todavía: un optimizador que devuelve Solve_Succeeded no está diciendo que el resultado sea bueno, solo que ha encontrado un punto estacionario de lo que le hayas escrito.

Qué compruebaResultado
0el paso de RK4 no decide el resultadocon diverge; con , error 6·10⁻⁵
0bla dinámica es la ya validada en LAMMPSel centro del rebaño coincide con error del 0,05 % del recorrido
1el gradiente por AD es correcto4,5·10⁻¹⁰ frente a diferencias finitas, y 18 veces más rápido
21 perro + 1 oveja, como en Escobedo et al.el perro se coloca detrás (cos = 0,999) a 42 unidades () y empuja
320 ovejas + 1 perro de 0,374 a 0,161; la dispersión final cae a la cuarta parte
4200 ovejas + 3 perrosla sección siguiente

Dos peldaños merecen comentario.

El paso del integrador no se elige a ojo. Con y la integración no es imprecisa: diverge, a 10²³⁰ y más. El perro tiene una escala de tiempo de y RK4 no la aguanta con pasos largos. Con ya está cerca del límite (error 0,46 en el estado final) y con cae a 6·10⁻⁵. Diez subpasos por intervalo de control, por tanto, no son prudencia: son el mínimo.

El caso de un perro y una oveja da el resultado más instructivo de todos. Si el perro empieza del lado equivocado —entre la oveja y el objetivo— y se arranca el optimizador de control nulo, IPOPT converge, con todas las de la ley (Solve_Succeeded), a la solución de abandonar: el perro se va lejos, su repulsión se anula, el gradiente también, y la oveja se queda donde está. Arrancando de la heurística, que sabe rodear, el mismo optimizador con el mismo coste encuentra la solución buena y lleva la oveja al objetivo.

1 perro, 1 ovejaarranque finaldistancia finalllega en
perro detrásfrío0,0870,6t = 37
perro detráscaliente0,0870,6t = 37
perro delantefrío1,599186,1no llega
perro delantecaliente0,1390,3t = 74

Dicho lo cual, conviene no generalizarlo, y este es justo el tipo de cosa que una escalera de pruebas sirve para desmentir: en el caso grande el arranque en caliente no hace falta. Repitiendo el problema de 200 ovejas partiendo de control nulo se llega al mismo sitio —distancia 2,2 en vez de 2,0, el mismo coste 0,130 y hasta un poco menos de esfuerzo—. Quien saca al optimizador del agujero allí no es la heurística: es el término de seguimiento, que le da gradiente desde el primer intervalo.

Qué sale

Las dos políticas, con la misma dinámica, el mismo estado inicial y la misma cota. Los paneles no son vídeos: el navegador reconstruye los 150 fotogramas de cada escena a partir de un binario y los dibuja en vivo. La barra va a cualquier instante y cada panel lleva debajo sus tres medidas.

heurística «empujar y girar»
Tres perros dando vueltas alrededor de un rebaño de doscientas ovejas que sigue lejos del objetivo.

control óptimo (CasADi + IPOPT)
El rebaño de doscientas ovejas ya sobre el objetivo, con los tres perros parados lejos.

  • los tres perros
  • 200 ovejas
  • objetivo
Izquierda: la heurística «empujar y girar» del TFM, con los tres perros repartidos en arco. Derecha: la trayectoria que devuelve IPOPT. Mismo horizonte (T = 400), misma cota (|a| ≤ 25) y mismas 200 ovejas de partida. Arrastra la barra para ir a cualquier instante.
Al final del horizonteheurísticaóptimo
coste 0,6390,130
distancia al objetivo86,92,0
cuándo llega a menos de 20nuncat = 60
distancia mínima86,90,5
dispersión (radio de giro²)524503
esfuerzo 428 30039 982

El óptimo no gana por poco: llega —la heurística, con 200 ovejas, ya no— y lo hace gastando diez veces menos. Y la diferencia se ve mejor en la animación que en la tabla: los perros de la heurística se pasan la mitad del tiempo rodeando el rebaño, porque su término de giro los manda a la línea exacta rebaño → objetivo y vuelve a mandarlos cada vez que el rebaño se desvía. Los perros del óptimo empujan en diagonal, aceptan que el rebaño no vaya recto, y corrigen al final.

Hay un detalle que dice mucho sobre qué es optimizar, y está en la curva del esfuerzo acumulado: el 81 % del gasto del óptimo ocurre en los primeros 50 de las 400 unidades de tiempo, que es cuando el rebaño pasa de 202 a 26 del objetivo. El resto del horizonte —350 unidades— solo sirve para recorrer esas últimas 26, con los perros prácticamente parados. La heurística hace lo contrario: su esfuerzo sube durante todo el horizonte, porque cada vuelta cuesta lo mismo que la anterior.

Seis paneles: arriba, las trayectorias de la heurística y del óptimo y una tabla con los números finales; abajo, tres curvas frente al tiempo: distancia al objetivo, dispersión y esfuerzo acumulado, con la heurística a trazos y el óptimo en línea continua.
Las mismas dos corridas, en curvas. La del esfuerzo acumulado es la que mejor resume el asunto: la heurística sube durante todo el horizonte; el óptimo gasta al principio y se detiene.

Lo que el coste te devuelve, al pie de la letra

Esta es la parte que de verdad se aprende escribiendo uno de estos problemas, y es la que no sale en la tabla de resultados.

El funcional pide dispersión pequeña al final. Eso es exactamente lo que el optimizador entrega, ni más ni menos: con 20 ovejas, la trayectoria óptima estira el rebaño en un arco que a mitad de camino alcanza una dispersión de unos 6 200 —diez veces la de partida; la heurística nunca pasa de 673— y lo recoge justo a tiempo para acabar en 164. Es correcto, cumple el coste al pie de la letra, y es desagradable de ver.

Con 200 ovejas no pasa, pero no porque el coste lo impida: la cohesión suma sobre todo el rebaño sin normalizar, así que con 200 individuos es siete veces más fuerte que con 30 y mantiene el grupo unido aunque nadie se lo pida. Es suerte, no diseño.

Lo que se arregla escribiendo lo que se quiere: añadiendo un peso sobre la dispersión a lo largo del horizonte, el rebaño se mantiene compacto todo el camino y encima acaba más cerca, a cambio de un 80 % más de esfuerzo.

Variantedistancia finaldispersión finalesfuerzotiempo
heurísticala política del TFM86,9524428 3002 s
el del enunciado, , , arranque caliente2,050339 982105 min
arranque fríoel mismo coste, partiendo de control nulo2,250337 420105 min
sin seguimiento: el coste solo mira el instante final3,1514120 377126 min
más peso a agrupar1,733357 601117 min
compacto todo el camino: la dispersión cuenta en cada instante1,436968 956116 min

Las cinco versiones del óptimo hacen lo mismo a grandes rasgos —empujar en diagonal y llegar pronto—; lo que compran los pesos altos es un rebaño más apretado, y lo pagan en esfuerzo. Cada una de esas filas son dos horas de IPOPT, que es la otra mitad de la moraleja: explorar el espacio de los pesos es caro, así que conviene saber qué se está pidiendo antes de pedirlo.

Qué no demuestra esto

Cinco límites, por orden de importancia:

No converge. IPOPT para en el máximo de iteraciones con la infactibilidad dual en 3,6·10⁻⁴, no en la tolerancia pedida. El grueso de la mejora está en las primeras 40 iteraciones y las últimas decenas apenas mueven el coste, así que para el resultado da igual; pero esto no es un óptimo global, ni siquiera un mínimo local certificado: es el mejor punto que IPOPT alcanzó en 105 minutos. Todo lo que dice el artículo es que ese punto es mucho mejor que la heurística.

Es lazo abierto. Una trayectoria de control calculada de antemano para un estado inicial concreto. Si una oveja se desvía, nadie corrige; si el estado inicial cambia, hay que volver a pagar las dos horas. La versión honesta sería MPC —reoptimizar cada pocos pasos sobre un horizonte corto—, que es justamente lo que hacen Ko y Zuazua con random batch, y es el motivo por el que el TFM se fue al aprendizaje por refuerzo: una política aprendida responde en microsegundos a cualquier estado.

Una condición inicial, y benévola. Los tres perros arrancan ya por detrás del rebaño. El peldaño 2 enseña lo que pasa cuando no es así —con un perro delante y arranque frío, el optimizador converge a abandonar—, pero esa variante no está corrida en el caso grande. Y el reparto inicial de las ovejas es una espiral de Fibonacci con un poco de ruido, no un sorteo uniforme: con 200 ovejas al azar quedan parejas pegadas, y una pareja pegada hace que la fuerza domine el primer instante.

La comparación favorece al óptimo en el terreno que mide el coste. Con 200 ovejas el óptimo gana en todo, pero en el peldaño de 20 ovejas y un perro gasta más: 49 956 de esfuerzo frente a 2 059 de la heurística, para acabar a 8,6 en vez de a 5,0 —con la dispersión final a la cuarta parte, que es lo que el coste le pide y la heurística no hace—. Comparar políticas con un número exige decir cuál, y aquí el número es con unos pesos concretos.

El modelo sigue siendo un modelo. Ovejas que son puntos sin visión, sin ruido y sin terreno, repulsión isótropa, cohesión de alcance largo y sin normalizar —de modo que las fuerzas crecen con el tamaño del rebaño—. Lo que se defiende aquí es la cadena numérica: la dinámica es la misma que se validó en LAMMPS (0,05 % de desvío en el centro del rebaño), el gradiente es correcto (4,5·10⁻¹⁰) y el paso del integrador está elegido, no heredado.

La segunda parte: rebaños que no son un disco

Todo lo de aquí empieza con 200 ovejas repartidas en un disco, que es el caso fácil: un rebaño compacto tiene un centro que lo representa, y por eso una política que solo mira el centro —la heurística— puede funcionar. La pregunta siguiente es qué pasa cuando no lo es: un rebaño en forma de media luna, partido en dos grupos, o repartido a lo largo de una línea.

Ahí el centro de masas deja de ser una descripción útil —puede caer en un sitio donde no hay ninguna oveja— y la heurística pierde la poca información con la que trabajaba. El control óptimo, en cambio, no necesita que el rebaño tenga forma de nada: ve las 200 posiciones.

El escenario ya está escrito en el repo (--escenario irregular): una banda curva con dos grumos densos unidos por un puente ralo, y los tres perros repartidos a mala idea —uno dentro del grumo grande, otro al otro lado de la banda y el tercero entre el rebaño y el objetivo, es decir, justo donde más estorba—.

Eso es Un rebaño que no es un disco, la segunda parte: allí la heurística no mueve el rebaño en absoluto —se queda a 264,5 del objetivo, donde empezó— y el optimizador lo reúne, lo estira para arrastrarlo y lo deja dentro gastando una quinta parte. Con dos lecciones de calibración que no se ven venir y tres horas y media de IPOPT que acaban mucho más lejos de converger que estas.

Cómo reproducirlo

Todo está en github.com/djoroya/sim-visuales, en control/pastoreo-optimo/. Un solo script:

./run.sh              # todo (el caso grande tarda ~2 h)
./run.sh escalera     # solo las comprobaciones (~5 min)
./run.sh grande       # solo el problema de 200 ovejas y 3 perros
./run.sh salidas      # binario del visor, poster e informe

Hace falta Python 3 con CasADi, numpy y matplotlib —el script crea el entorno virtual la primera vez— y, opcionalmente, LAMMPS para el contraste del peldaño 0b. La escalera entera son cinco minutos; el caso grande, las dos horas de IPOPT de la tabla.

Lo más instructivo que se puede hacer con él es tocar los pesos: --lam, --bet, --gam, --lam-t. Cada ejecución tarda lo suyo, pero la lección llega antes de que acabe.

Detalle técnicoCómo caben las dos simulaciones en esta página

Cada escena son 150 fotogramas de 203 individuos, y hay dos. En disco eso son varios megabytes de texto; lo que llega al navegador son 238 KiB sin comprimir y 174 con gzip, con el mismo truco que el artículo anterior: posiciones cuantizadas a 16 bits sobre la caja común, con el byte alto y el byte bajo en planos separados para que gzip tenga algo que morder. El error de reconstrucción es de 0,003 unidades de longitud, tres órdenes de magnitud por debajo del radio con el que se dibuja una oveja.

Es seis veces y media lo que pesaba la trayectoria del artículo anterior, y buena parte de la culpa es que son dos trayectorias: el lado a lado se paga entero. A cambio, el visor sigue siendo un canvas 2D sin dependencias —son 203 círculos por panel— y la página no descarga nada de esto hasta que la figura está a 300 px de entrar en pantalla. Lo que se ve antes, y lo único que se ve sin JavaScript, son los dos pósters PNG (14 y 9 KiB), que los genera un script a partir del propio binario, con la misma paleta y los mismos radios que el visor y en el mismo instante en el que el visor se queda parado si has pedido menos movimiento. Así, al montarse, no se mueve nada.


Referencias. J. Oroya, Aprendizaje por refuerzo aplicado al problema del pastoreo, TFM, Máster en Ingeniería Computacional y Sistemas Inteligentes, UPV/EHU, 2020; director: Roberto Santana — modelo, parámetros y heurística; está en la página de artículos con su PDF. · R. Escobedo, A. Ibáñez, E. Zuazua, Optimal strategies for driving a mobile agent in a “guidance by repulsion” model, Commun. Nonlinear Sci. Numer. Simul. 39 (2016) 58–72 (arXiv:1509.05008) — el caso de dos agentes del peldaño 2. · D. Ko, E. Zuazua, Asymptotic behavior and control of a “guidance by repulsion” model (arXiv:1911.01133) y Model predictive control with random batch methods for a guiding problem (arXiv:2004.14834) — el lazo cerrado que aquí no está. · J. Andersson et al., CasADi: a software framework for nonlinear optimization and optimal control, Math. Prog. Comp. 11 (2019) 1–36. · Código original en MATLAB, de la Cátedra de Matemática Computacional de Deusto: DeustoTech/GuidanceByRepulsion.

Etiquetas: control óptimo · CasADi · dinámica de partículas · control

← Todos los artículos