Validación y Análisis · Tema 02

Verificación del modelo conceptual

El bug más caro es el que está en el papel: revisar lógica, supuestos, unidades y diagramas antes de programar cuesta minutos y ahorra semanas.

01 · Punto de partida

Programar un error conceptual lo vuelve más rápido, no más correcto

En el Tema 1 distinguimos verificar (¿construimos bien?) de validar (¿construimos lo correcto?). La verificación empieza mucho antes del código: en el modelo conceptual, es decir, el documento —texto, diagrama, ecuaciones y supuestos— que dice qué se va a programar.

Si ese documento dice «las llegadas son constantes» cuando hay hora pico, o «el tanque es infinito» cuando tiene tope, ningún test unitario lo detectará: el programa será fiel a una mala idea.

  • ¿Las reglas son coherentes entre sí o se contradicen?
  • ¿Falta algún evento, estado o condición de borde?
  • ¿Los supuestos están escritos o viven solo en tu cabeza?
  • ¿Las unidades y rangos cierran de punta a punta?

02 · Definición

Qué es y qué no es

El modelo conceptual incluye: objetivo, frontera, variables y parámetros con unidades, reglas de actualización, condiciones iniciales y de contorno, supuestos y rango de validez declarado.

☑

Completo

Todo estado tiene una regla. No hay «después vemos qué pasa si el tanque se vacía o la cola explota».

⇄

Coherente

Ninguna regla contradice a otra. Si la entrada es por minuto, la salida no puede estar por hora sin conversión.

✎

Explícito

Cada simplificación está escrita como supuesto numerado (S1, S2…), con su riesgo si se viola.

Verificar esto no usa datos reales ni código: usa lectura cruzada, diagramas, análisis dimensional y recorridos mentales de casos borde. La validación con datos vendrá después (Temas 5–8).

03 · Técnicas

Cinco chequeos que siempre rinden

  1. 1
    Lectura estructurada (walkthrough).

    Otra persona recorre la especificación en voz alta con un checklist. El autor no defiende: anota. 30 minutos detectan el 60% de los defectos conceptuales.

  2. 2
    Diagrama de estados y flujos.

    Dibujar cajas y flechas obliga a decidir: ¿qué entra, qué se acumula, qué sale? Toda flecha sin origen o destino es un hueco conceptual.

  3. 3
    Análisis dimensional.

    Revisar unidades: nivel [litros] + caudal [litros/paso] × pasos [pasos] = [litros]. Si sumás litros con litros/minuto, el concepto está roto.

  4. 4
    Casos borde mentales.

    Probar en papel: cero, tope, negativo, infinito, primer y último paso. ¿Qué hace la regla? Si no lo dice, falta especificación.

  5. 5
    Matriz de supuestos.

    Tabla S1…Sk con columnas: supuesto, por qué se asume, qué lo rompería, qué pasa si se rompe. Lo no escrito no existe.

Matriz mínima de supuestos para un tanque.
IDSupuestoSe rompe si…Efecto
S1Flujos constantes por pasoHay hora pico o evaporaciónBalance real ≠ supuesto → sesgo
S2Nivel entre 0 y topeTope mal medidoDesborde o piso no modelado
S3Sin pérdidas ni retrasosHay filtraciónSobreestima el nivel

04 · Defectos típicos

El catálogo de horrores del papel

Incompletitud

Falta qué hacer

Sin condición inicial, sin regla cuando la cola está vacía, sin tope. El programador inventa sobre la marcha y cada versión inventa distinto.

Incoherencia

Reglas que chocan

«Llegadas Poisson» en el texto pero «llegadas constantes» en la fórmula. Unidades mezcladas. Horizonte de 12 pasos con calentamiento de 20.

Supuesto oculto
Decisión no escrita que el código asume igual («nadie abandona la fila»). Se verifica obligando a escribir S1…Sk.
Ambigüedad
«Atención rápida» sin número. Se verifica pidiendo rango y unidad: «atención ~ Exp(media 4 min)».
Alcance difuso
Sin rango de validez («vale para 0–30 clientes/hora»). Se verifica exigiendo la frase «este modelo no debe usarse cuando…».

05 · Representación en Python

Auditar la especificación con código

Paradójico pero útil: usamos Python no para simular, sino para verificar el documento. La especificación se escribe como datos y un verificador chequea completitud, rangos y unidades antes de programar el simulador.

Python en tu navegador. Este verificador corre localmente con Pyodide. Rompé a propósito un campo y observá qué chequeo falla.

spec = {
    "nombre": "Tanque con tope",
    "variables": {"nivel": {"unidad": "litros", "min": 0, "max": 14}},
    "parametros": {
        "entrada": {"valor": 3, "unidad": "litros/paso"},
        "salida": {"valor": 2, "unidad": "litros/paso"},
        "tope": {"valor": 14, "unidad": "litros"},
        "nivel_inicial": {"valor": 10, "unidad": "litros"},
    },
    "supuestos": ["S1: flujos constantes", "S2: nivel en [0, tope]"],
    "regla": "nivel(t+1) = max(0, min(tope, nivel(t) + entrada - salida))",
}

def verificar_spec(spec):
    errores = []
    p = spec["parametros"]
    v = spec["variables"]["nivel"]
    if not spec.get("supuestos"):
        errores.append("Falta lista de supuestos S1..Sk")
    if not spec.get("regla"):
        errores.append("Falta regla de actualización")
    if p["tope"]["valor"] <= 0:
        errores.append("Tope debe ser > 0")
    if not (v["min"] <= p["nivel_inicial"]["valor"] <= v["max"]):
        errores.append("Condición inicial fuera de [min, max]")
    if p["tope"]["valor"] != v["max"]:
        errores.append("Tope inconsistente con max de la variable")
    u = {p["entrada"]["unidad"], p["salida"]["unidad"]}
    if u != {"litros/paso"}:
        errores.append(f"Unidades de flujo inconsistentes: {u}")
    return errores


print(verificar_spec(spec) or "Spec OK: completa y coherente")

Probá cambiar el tope a 10 sin cambiar el max, o borrar los supuestos: el verificador lo detecta antes de simular.

Chequeo de casos borde en papel, ejecutado

def regla(nivel, entrada, salida, tope):
    return max(0, min(tope, nivel + entrada - salida))

# Casos borde que la especificación debe definir
bordes = [
    (0, 3, 2, 14, 1),    # arranca vacío
    (14, 3, 2, 14, 14),  # arranca lleno y quiere desbordar
    (2, 1, 5, 14, 0),    # quiere ir a negativo
]
for niv, e, s, tope, esperado in bordes:
    r = regla(niv, e, s, tope)
    estado = "OK" if r == esperado else f"FALLA (dio {r})"
    print(f"nivel={niv} +{e}-{s} tope={tope} -> {r} [{estado}]")

06 · Exploración

Laboratorio: ¿pasa tu especificación la auditoría?

Mové los parámetros de la especificación del tanque y observá los 5 chequeos conceptuales. El gráfico muestra la trayectoria que la regla promete, la línea de tope y el piso en cero. Los puntos rojos son violaciones que un buen papel jamás debería permitir.

EXPERIMENTO 02

Auditoría del modelo conceptual

5 chequeos · 0 violaciones = spec verificada

Los resultados numéricos aparecen debajo.
Chequeos aprobados5 / 5
Nivel final (12 pasos)14,00
Violaciones0
VeredictoVerificada

Especificación verificada: inicial dentro del rango, tope positivo y regla con piso y techo.

La curva muestra el nivel prometido por la regla con piso en cero y techo en el tope. La línea punteada es el tope declarado. Si el nivel inicial supera el tope o el tope es inválido, la especificación se rechaza antes de programar.

Preguntas para explorar

  1. Subí el nivel inicial a 20 con tope 14. ¿Qué chequeo falla? ¿Por qué ningún código puede arreglarlo?
  2. Bajá el tope a 4 con inicial 10. ¿Qué le pasa a la trayectoria? ¿Qué supuesto (S2) quedó violado?
  3. Poné entrada 0 y salida 6 con inicial 10. ¿La regla con piso en cero está bien especificada? ¿Qué pasaría si el papel no mencionara el piso?
Ver respuestas sugeridas
  1. Falla «inicial en [0, tope]». Es un defecto del papel: el programador no puede adivinar si debe recortar, rechazar o ampliar el tope. Se devuelve a la especificación.
  2. La trayectoria queda aplastada contra el techo: el sistema vive saturado. S2 («nivel en [0, tope]») se cumple a duras penas, pero el diseño es inútil: hay que revisar el tope o el horizonte.
  3. Con piso bien especificado el nivel baja 10, 4, 0, 0… Sin piso, el papel permitiría niveles negativos (−2, −8…), físicamente imposibles: cada implementación inventaría su propio parche.

07 · Comprensión

Confusiones frecuentes

«Verificar el papel es perder tiempo»

Es la actividad con mayor retorno: una hora de walkthrough evita días de depuración y semanas de recalibración sobre un concepto roto.

«Si el diagrama es lindo, el modelo es correcto»

Un diagrama prolijo puede ocultar supuestos no escritos. La verificación exige checklist, unidades y casos borde, no solo estética.

«Los supuestos son obvios, no hace falta escribirlos»

Lo obvio para vos es invisible para el que programa y para el que valida. S1…Sk siempre escritos, siempre numerados.

«La verificación conceptual usa datos»

No: usa coherencia interna. Los datos aparecen en la validación (Temas 5–8). Mezclarlas lleva a ajustar el papel a los datos sin declarar la calibración.

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: detectar la especificación incompleta

Esta spec no declara qué hacer cuando la cola está vacía. Ejecutá el verificador y completá la regla.

spec_cola = {
    "variables": {"clientes_en_cola": {"min": 0, "max": 100}},
    "parametros": {"llegadas": 5, "atendidos": 3, "inicial": 0},
    "supuestos": [],
    "regla": "",
}

def auditar(spec):
    faltas = []
    if not spec["supuestos"]:
        faltas.append("sin supuestos")
    if not spec["regla"]:
        faltas.append("sin regla para cola vacía")
    if spec["parametros"]["inicial"] < 0:
        faltas.append("inicial negativo")
    return faltas

print(auditar(spec_cola))
Ver solución razonada

Devuelve ['sin supuestos', 'sin regla para cola vacía']. La spec debe agregar S1 («llegadas y atenciones constantes por paso») y la regla cola(t+1) = max(0, cola + llegadas − atendidos). Sin eso, cada programador implementa su propio piso.

Ejercicio 2: cazar la incoherencia de unidades

La entrada está por minuto y la salida por hora, pero la regla las suma directo. Encontrá el error con el verificador.

spec_mix = {
    "parametros": {
        "entrada": {"valor": 3, "unidad": "litros/minuto"},
        "salida": {"valor": 120, "unidad": "litros/hora"},
    }
}
e = spec_mix["parametros"]["entrada"]["unidad"]
s = spec_mix["parametros"]["salida"]["unidad"]
print("entrada:", e, "| salida:", s)
print("¿Coherente?", e == s, "-> convertir antes de sumar")
Ver solución

No son coherentes: litros/minuto ≠ litros/hora. La spec debe fijar una unidad común (ej. litros/paso) y la conversión explícita (120 litros/hora = 2 litros/minuto si el paso es 1 minuto). Defecto conceptual, no de tipeo.

Ejercicio 3: escribir la matriz de supuestos

Convertí esta frase vaga en tres supuestos numerados con condición de ruptura.

Ver una posible respuesta
supuestos = [
    "S1: llegadas constantes = 5/paso; se rompe si hay hora pico",
    "S2: nadie abandona la fila; se rompe si la espera > 15 min",
    "S3: un solo servidor sin fallas; se rompe si hay cortes",
]
for s in supuestos:
    print(s)

Cada supuesto declara su ruptura y, por tanto, el límite de validez. Cuando la sucursal abra una segunda caja o haya abandonos, sabrás que S2–S3 cayeron y el modelo debe revisarse.

09 · Síntesis

Ideas para recordar

  • La verificación conceptual audita el papel: completitud, coherencia y explicitud, sin datos ni código.
  • Walkthrough, diagramas, análisis dimensional, casos borde y matriz S1…Sk son las cinco herramientas básicas.
  • Todo lo no escrito (supuesto oculto, regla de borde, unidad) será inventado por el programador de forma inconsistente.
  • En Python, la spec puede verificarse como datos antes de simular: el verificador corre primero.
  • Un papel verificado no garantiza validez real, pero un papel no verificado garantiza problemas.

En el próximo tema pasaremos a la verificación de la implementación: cómo comprobar que el programa ejecuta fielmente ese papel ya auditado.