01 · Punto de partida
Integrar en serie rompe la cadena
El SIR tiene tres estados que se pasan masa en el mismo instante. Si integrás S hasta el final y después I con S promediada, el contagio usa un pasado que I nunca vivió: el pico se corre y el ataque miente. La regla de oro: todas las tasas primero, todos los estados después, mismo dt.
Este tema convierte a Euler y RK4 en versión vectorial: las mismas firmas, con dicts en vez de flotantes.
- ¿Qué estados comparten cada tasa?
- ¿Avanzo el vector junto con el mismo dt?
- ¿Se conserva S + I + R = N en cada nodo?
- ¿Euler con dt chico valida al RK4 de producción?
02 · Definición
¿Qué es simular un sistema acoplado?
El mismo paso, en plural:
Vector x
El paquete [S, I, R] o [A, B]: cada nodo guarda todos los estados en el mismo t.
Tasas juntas
f(x, t) devuelve el dict completo calculado con el mismo x: nada de mezclar tiempos.
Conservación
S + I + R = N en cada nodo: si deriva, el paso mezcla instantes.
Sin exacta (SIR no tiene), el testigo es la convergencia entre dts y la conservación en cada nodo.
| Método (dt) | Pico I | Lectura |
|---|---|---|
| Euler dt = 2 | ≈ 320 (pasado) | Grueso: se pasa el pico. |
| Euler dt = 0,5 | ≈ 301 (referencia) | Fino: valida producción. |
| RK4 dt = 1 | ≈ 300 (producción) | Clava con 4×160 f. |
| En serie (mal) | corrido y roto | S + I + R deriva: prohibido. |
03 · Anatomía
Tasas, vectores y verificación
Simular acoplados bien pide tres hábitos:
- Tasas del mismo instante
- Calcular todo f(xₙ) antes de tocar ningún estado: el contagio usa S e I del mismo t.
- Etapas vectoriales
- En RK, cada kᵢ es un dict [dS, dI, dR]: promediar escalares rompe el acople.
- Conservación por nodo
- S + I + R = N siempre: el assert más barato del curso.
- Convergencia sin exacta
- dt vs dt/2 en pico y ataque: si no converge, hay bug o rigidez.
Junto (bien)
Vector que avanza unido
Tasas con xₙ, estados con las mismas tasas: la cadena S → I → R cierra y conserva.
En serie (mal)
Estados de tiempos mezclados
I usa S ya actualizado: el contagio vive en el futuro. El pico se corre y la masa deriva.
04 · Acoplados del curso
El mismo vector, cinco cadenas
Cada sistema se integra con la misma receta:
- 1Cascada A → B.
B hereda de A con retardo: conserva q en régimen.
- 2SIR.
S → I → R cerrada: conserva N en cada nodo.
- 3Resorte (x, v).
Ciclo que espirala: semi-implícito o RK conservan energía mejor.
- 4SEIR.
Latencia E que retrasa el pico: cuatro estados juntos.
- 5Dos tanques.
Niveles que se pasan caudal: mismos nodos, dos curvas.
05 · Representación en Python
Euler y RK4 vectoriales con dicts
En Python, el estado es dict y cada kᵢ también: sumar dicts con pesos es todo el secreto.
Python en tu navegador. Estos bloques se ejecutan localmente con Pyodide, sin instalar nada. Subí dt a 2 y observá que el pico se pasa.
def f_sir(e, beta=0.3, gamma=0.1, n=1000):
c = beta * e["S"] * e["I"] / n
return {"S": -c, "I": c - gamma * e["I"], "R": gamma * e["I"]}
def euler_vec(f, x0, t_final, dt):
x, t = dict(x0), 0.0
while t < t_final - 1e-9:
h = min(dt, t_final - t)
d = f(x, t)
x = {k: x[k] + d[k] * h for k in x}
t += h
return x
x0 = {"S": 990.0, "I": 10.0, "R": 0.0}
fin = euler_vec(f_sir, x0, 160, dt=0.5)
print({k: round(v, 1) for k, v in fin.items()}, "suma:", round(sum(fin.values()), 1))
S ≈ 59 · I ≈ 0 · R ≈ 941, suma 1000: conserva y ataca el 94 %.
Pico con historia y conservación
El pico de I y el assert de masa en cada nodo:
def brote(dt=0.5, dias=160):
x, t, pico = dict(x0), 0.0, 10.0
while t < dias - 1e-9:
h = min(dt, dias - t)
d = f_sir(x, t)
x = {k: x[k] + d[k] * h for k in x}
assert abs(sum(x.values()) - 1000) < 1e-6
pico = max(pico, x["I"])
t += h
return round(pico, 1)
print(brote(2.0), brote(0.5))
06 · Exploración
Laboratorio: el pico se mueve con dt
SIR (N = 1000, I₀ = 10, β con tu slider, γ = 0,1) integrado con Euler dt grueso (teal, se pasa) y Euler dt = 0,25 fino (blanco, referencia). Achicá tu dt y observá que el pico teal gatea hacia el blanco: la convergencia valida sin exacta.
Convergencia del pico
SIR · Euler(dt) vs Euler(0,25) · γ = 0,1
Con dt 2 el pico se pasa en 20,30: el paso grueso exagera la joroba.
Ambas curvas conservan N = 1000 en cada nodo: la diferencia es solo resolución, no masa perdida.
Preguntas para explorar
- Con dt = 0,25, ¿la diferencia se achica? ¿A cuánto?
- Con β = 0,5, ¿el pico se pasa más o menos con dt = 2?
- Si la suma diera 998, ¿qué bug buscarías primero?
Ver respuestas sugeridas
- Sí, a ≈ 0: tu dt iguala la referencia. Convergencia firmada.
- Más: más curvatura, más overshoot del paso grueso.
- Actualización en serie (S nueva para I): tasas de tiempos mezclados. Todo f primero.
07 · Comprensión
Confusiones frecuentes
«Cada estado con su dt»
No: un solo reloj para todo el vector. dts distintos desincronizan el acople.
«Conserva al final, alcanza»
Debe conservar en cada nodo: al final puede cancelar errores de signo. El assert va adentro del bucle.
«RK4 vectorial es otro método»
Es el mismo con dicts: cada kᵢ evalúa todo f. La receta no cambia, el contenedor sí.
«Sin exacta no hay error»
Hay convergencia (dt vs dt/2) y conservación: dos testigos que no piden fórmula.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: tasas del mismo instante
Con S = 990, I = 10, calculá dS, dI, dR con β = 0,3 y γ = 0,1. ¿Suman cero?
d = f_sir({"S": 990.0, "I": 10.0, "R": 0.0})
print({k: round(v, 2) for k, v in d.items()}, "suma:", round(sum(d.values()), 6))
Ver solución
−2,97 · +1,97 · +1,0, suma 0: lo que sale de S entra a I y de I a R. Cadena cerrada.
Ejercicio 2: un paso vectorial
Aplicá un paso Euler dt = 1 desde (990, 10, 0). ¿Conserva N?
x = {"S": 990.0, "I": 10.0, "R": 0.0}
d = f_sir(x, 0)
x1 = {k: x[k] + d[k] * 1.0 for k in x}
print({k: round(v, 2) for k, v in x1.items()}, "suma:", round(sum(x1.values()), 1))
Ver solución
(987,03 · 11,97 · 1,0), suma 1000: el vector avanzó junto y la masa cierra.
Ejercicio 3: convergencia del pico
Compará el pico con dt = 2 y dt = 0,5. ¿Cuánto se pasa el grueso?
Ver una posible respuesta
print("grueso:", brote(2.0), "fino:", brote(0.5))
Grueso ≈ 320 frente a fino ≈ 301: el paso grueso exagera la joroba en ~20. Convergencia sin exacta.
09 · Síntesis
Ideas para recordar
- Vector junto: todas las tasas con xₙ, todos los estados con esas tasas, mismo dt.
- kᵢ vectoriales en RK: cada etapa evalúa todo f, no un estado.
- Conservación por nodo (S + I + R = N) como assert barato.
- Sin exacta: convergencia dt/dt/2 en pico y ataque como testigo.
- En Python: dicts + assert + brote(dt) para el pico.
En el próximo tema elegiremos la representación: continuo frente a discreto.