01 · Punto de partida
Del plan a la pista
En el Tema 32 escribimos el plan: qué escenarios, cuántas réplicas y en qué orden. En el Tema 31 dejamos el motor listo. Ejecutar es juntarlos: lanzar cada corrida con su escenario y su semilla, controlar cuánto tiempo simulado corre cada una y guardar lo necesario para analizar después.
Parece trámite, pero aquí se arruinan muchos estudios: corridas que arrancan con restos de la anterior, promedios contaminados por el arranque en vacío y resultados que nadie puede rastrear hasta el escenario que los produjo.
- ¿Cuánto tiempo simulado corre cada corrida?
- ¿Qué parte del arranque se descarta como transitorio?
- ¿Cada corrida arranca limpia y aislada?
- ¿Qué se guarda de cada corrida y con qué traza?
02 · Vocabulario mínimo
Corrida, horizonte y registro
Corrida
Una ejecución del simulador con un escenario y una semilla: la unidad mínima de evidencia.
Horizonte
Cuánto tiempo simulado se corre: pasos fijos, fecha de fin o condición de corte.
Registro
Lo que se guarda: métricas compactas más traza (escenario, semilla, versión, estado).
| Qué | Ejemplo | Para qué sirve |
|---|---|---|
| Métricas | Espera promedio desde t = 20 | Comparar escenarios |
| Traza | Escenario, semilla, versión, horizonte | Repetir la corrida idéntica |
| Estado final | OK, falló, recortada por corte | No mezclar éxitos con accidentes |
| Historia resumida | Serie o muestra, no todo crudo | Auditar sin llenar el disco |
03 · Tres controles
Tiempo, aislamiento y registro
- 1Tiempo.
Fijá horizonte, warm-up y condición de corte antes de correr. El arranque en vacío no representa el régimen.
- 2Aislamiento.
Cada corrida arranca limpia: estado reiniciado, generador propio, sin archivos compartidos a medio escribir.
- 3Registro.
Guardá métricas + traza en el mismo acto. El resultado sin su escenario y su semilla es un huérfano.
Guardar todo crudo
Pesado e inmanejable
Cada paso de cada corrida al disco. Auditable en teoría, impagable en la práctica: gigabytes que nadie vuelve a leer.
Métricas + traza
Liviano y repetible
Se guarda lo compacto y lo que permite regenerar el resto. Menos disco, misma evidencia, réplica exacta si hace falta.
- Horizonte
- Tiempo simulado que dura la corrida: pasos, fecha o condición de fin.
- Warm-up
- Tramo inicial que se corre pero no se mide, para salir del arranque artificial.
- Condición de corte
- Regla de parada anticipada: vaciado, tope, presupuesto de cómputo.
- Traza
- Ficha de la corrida: escenario, semilla, versión, horizonte y estado final.
04 · Protocolo
La coreografía de cada corrida
Inicializar con escenario y semilla propios; calentar sin medir; medir en régimen; cerrar con estado explícito (OK o motivo de corte); guardar métricas con su traza. El Tema 34 analizará esos registros; el Tema 41 exigirá repetirlos.
05 · En Python
Correr con warm-up y traza
La corrida simula el horizonte completo pero mide solo desde el warm-up. Así el transitorio no contamina la métrica:
Python en tu navegador. Horizonte, descarte y registro con stdlib puro.
import random
def corrida(n0, entradas, salida, semilla, warmup=0):
rng = random.Random(semilla) # azar propio y aislado
n = n0
historia = []
for e in entradas:
ruido = rng.choice([-1, 0, 1])
n = max(n + e + ruido - salida, 0)
historia.append(n)
medida = historia[warmup:]
return {"semilla": semilla, "media": sum(medida) / len(medida),
"historia": historia, "estado": "OK"}
print(corrida(0, [3] * 10, 2, semilla=7, warmup=3))
El lote ejecuta el plan entero
El lote recorre las corridas del Tema 32, atrapa fallos con estado explícito y devuelve una tabla lista para analizar:
def lote(corridas, horizonte=10):
tabla = []
for c in corridas:
try:
r = corrida(0, [3] * horizonte, 2, c["semilla"], warmup=3)
tabla.append({"escenario": c["escenario"], **r})
except Exception as e:
tabla.append({"escenario": c["escenario"], "estado": f"FALLO: {e}"})
return tabla
plan = [{"escenario": (1, "alta"), "semilla": 7000},
{"escenario": (2, "alta"), "semilla": 7001}]
print(len(lote(plan)), "corridas registradas")
06 · Exploración
Laboratorio: el arranque engaña
El sistema arranca vacío y converge a su régimen. Si medís desde el paso cero, el promedio sale artificialmente bajo. Movete el horizonte y el warm-up y observá cómo la media medida se acerca a la real.
Medir en régimen
calentar → medir → guardar
Con warm-up 15, el arranque deja de tirar la media hacia abajo.
Zona sombreada: warm-up que se corre pero no se mide. Línea clara: media contaminada. Línea amarilla: media en régimen.
Preguntas para explorar
- Con warm-up en 0, ¿la media queda por encima o por debajo del régimen? ¿Por qué?
- Subí el warm-up a 30. ¿El sesgo se achica? ¿A qué costo?
- Alargá el horizonte a 100 con warm-up 15. ¿El descarte «cuesta» lo mismo en proporción?
Ver respuestas sugeridas
- Por debajo: arrancar en cero tira el promedio hacia abajo aunque el régimen sea alto.
- Sí, casi desaparece, pero se mide menos: 30 de cada 60 pasos van a la basura. Equilibrio entre sesgo y muestra.
- No: 15 de 100 es 15%, contra 25% con horizonte 60. El horizonte largo diluye el costo del warm-up.
07 · Comprensión
Confusiones frecuentes
«Más pasos siempre es mejor»
Más horizonte sin warm-up solo promedia más arranque con más régimen: el sesgo persiste. Largo sirve si el warm-up está bien puesto.
«El transitorio se ve a ojo y listo»
El ojo ayuda a elegirlo, pero debe quedar escrito como parámetro (warmup = 15) y aplicado igual a todas las corridas comparadas.
«Guardar todo crudo es más seguro»
Es más disco, más tiempo y más filtraciones de datos personales o sensibles. Métricas + traza regeneran lo necesario.
«Si una corrida falla la descarto en silencio»
No: se registra como fallo con su motivo. Los fallos silenciosos sesgan: solo «sobreviven» los escenarios que anduvieron.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: medir con y sin warm-up
Corré 10 pasos desde cero y compará la media total con la media desde el paso 3.
historia = [0, 2, 4, 6, 7, 8, 8, 9, 9, 9]
total = sum(historia) / len(historia)
regimen = sum(historia[3:]) / len(historia[3:])
print("total:", total)
print("régimen:", regimen)
Ver solución razonada
El total da 6.2 y el régimen 8.0: el arranque en 0, 2 y 4 arrastra el promedio casi dos puntos hacia abajo.
Ejercicio 2: descartar por slicing
Escribí la función que separa warm-up de medición y devolvé ambas partes.
def partir(historia, warmup):
return historia[:warmup], historia[warmup:]
print(partir([0, 2, 4, 6, 8, 9], 3))
Ver solución
Da ([0, 2, 4], [6, 8, 9]): lo primero se corrió pero no se mide; lo segundo es la muestra válida.
Ejercicio 3: lote que no pierde fallos
Simulá un lote de 3 corridas donde una falla y verificá que quede registrada con estado.
Ver una posible respuesta
tabla = []
for semilla in [7000, 7001, 7002]:
try:
if semilla == 7001:
raise ValueError("corte por presupuesto")
tabla.append({"semilla": semilla, "estado": "OK"})
except Exception as e:
tabla.append({"semilla": semilla, "estado": f"FALLO: {e}"})
print(tabla)
Las tres corridas quedan en tabla, una con FALLO: corte por presupuesto: el análisis decidirá después, con el registro completo a la vista.
09 · Síntesis
Ideas para recordar
- Ejecutar es inicializar, calentar, medir, cerrar y guardar, corrida por corrida.
- El horizonte y el warm-up se fijan antes: el arranque no se promedia.
- Cada corrida va aislada, con semilla propia y estado reiniciado.
- Se guardan métricas + traza + estado: liviano, auditable y repetible.
- En Python: bucle sobre el plan,
random.Random(semilla)por corrida ytry/exceptcon estado.
En el próximo tema leeremos esos registros: el análisis de resultados.