Saltar a contenido

Sistema de Validación Cruzada MATLAB ↔ Python

Estado 2026-05-05

La validación directa de fixtures se ejecutó con python3 scripts/validate_matlab_fixtures.py después de corregir la convención del ángulo de codo lateral para respetar MATLAB.

Resultado:

  • Fixtures comparables: 19
  • Passing: 19
  • Failing: 0
  • Skipped: 1 (TC_ANT_009 shoulder_angle, sin expected_output MATLAB)

El suite pytest no pudo ejecutarse en el Python del sistema porque falta instalar pytest. El script directo queda como verificación mínima reproducible hasta completar el entorno.

🎯 Objetivo Crítico

Preservar la validez matemática del código MATLAB original que fue evaluado y aprobado por estadistas y matemáticos.

La migración a Python NO debe introducir errores numéricos ni cambios en los algoritmos biomecánicos. Cada cálculo debe replicar exactamente el comportamiento de MATLAB.


📋 Estado Actual

Vista Anterior: ✅ VALIDADA

Cálculo MATLAB Líneas Python Función Estado Precisión
Ángulo de cabeza 1526-1543 angle_with_horizontal ✅ PASS <1e-10
Distancia EFM - horizontal_distance_cm ✅ PASS <1e-10
Distancia EN - horizontal_distance_cm ✅ PASS <1e-10
Distancia PM - horizontal_distance_cm ✅ PASS <1e-10
Ángulo de tronco 554-560 trunk_angle ✅ PASS <1e-10
Ángulo de pelvis 797-820 pelvis_angle ✅ PASS <1e-10
Ángulo Q derecho 910-914 q_angle ✅ PASS <1e-10
Ángulo Q izquierdo 946-950 q_angle ✅ PASS <1e-10

Total: 8/8 tests pasando

Vista Posterior: ✅ Script listo, ⏳ Pendiente datos

Archivos fuente: - old/matlab/InterfazMedicionEspaldaJulio2024v2 (1).m

Script generado: matlab_validation/generate_test_cases_posterior.m

Cálculos extraídos (7 casos): - TC_POST_001: Ángulo calcáneo izquierdo (líneas 1499-1522) - TC_POST_002: Ángulo calcáneo derecho (líneas 1673-1697) - TC_POST_003: Desbalance coronal C7 (líneas 942-948) - TC_POST_004: Desbalance coronal T7 (líneas 1013-1017) - TC_POST_005: Desbalance coronal L5 (líneas 1084-1088) - TC_POST_006: Ángulo pelvis posterior (líneas 1238-1251) - TC_POST_007: Ángulo cabeza posterior (líneas 866-890)

Próximo paso: Completar puntos de muestra con datos reales de paciente

Vista Perfil Derecho: ⏳ PENDIENTE

Archivos fuente: - old/matlab/InterfazMedicionPerfilDerechoJulio2024 (1).m

Cálculos a validar: - Tangente dorsal - Tangente sacro - Ápex cervical - Ápex lumbar - Alineación sagital - Traslación pélvica

Vista Perfil Izquierdo: ⏳ PENDIENTE

Archivos fuente: - old/matlab/InterfazMedicionPerfilIzquierdoJulio2024 (1).m

Cálculos a validar: - (Idénticos a perfil derecho, validar simetría)


🛠️ Metodología de Validación

Paso 1: Extraer Algoritmo MATLAB

  1. Localizar función en código .m (líneas específicas)
  2. Identificar variables de entrada
  3. Copiar algoritmo EXACTO (incluyendo estructuras if/else, signos, etc.)
  4. Documentar número de líneas para referencia

Ejemplo:

% InterfazMedicionFrenteJulio2024v2.m, líneas 554-560
A = [XEE-XPSP, YEE-YPSP];
B = [XPSP-XPSP, YEE-YPSP];  % Vector vertical
gradosTronco = rad2deg(acos(dot(A,B)/(norm(A)*norm(B))));

if XEE < XPSP
    gradosTronco = gradosTronco;  % Positivo
else
    gradosTronco = -gradosTronco;  % Negativo
end

Paso 2: Implementar en Python

def trunk_angle(p_sp: Point, p_ee: Point) -> float:
    """Angle between the vertical through the Escotadura and the line EE-PSP.

    MATLAB Reference: InterfazMedicionFrenteJulio2024v2.m, líneas 554-560
    Validated by: Estadistas (original)
    """
    dx = p_ee[0] - p_sp[0]
    dy = p_ee[1] - p_sp[1]

    if dy == 0:
        return 90.0 if dx < 0 else -90.0

    # Vector from PSP to EE and a vertical vector through PSP
    a = (dx, dy)
    b = (0.0, dy)  # Vertical
    dot = a[0] * b[0] + a[1] * b[1]
    mag_a = math.hypot(*a)
    mag_b = math.hypot(*b)
    angle = math.degrees(math.acos(dot / (mag_a * mag_b)))

    return angle if dx < 0 else -angle

Paso 3: Generar Caso de Prueba desde MATLAB

Ejecutar matlab_validation/generate_test_cases.m con datos reales:

% Cargar puntos de un caso validado
XPSP = 493.8188;
YPSP = 1182.7;
XEE = 486.1345;
YEE = 694.5977;

% Ejecutar algoritmo MATLAB
A = [XEE-XPSP, YEE-YPSP];
B = [XPSP-XPSP, YEE-YPSP];
gradosTronco = rad2deg(acos(dot(A,B)/(norm(A)*norm(B))));
if XEE < XPSP
    gradosTronco = gradosTronco;
else
    gradosTronco = -gradosTronco;
end

fprintf('Resultado MATLAB: %.15f\n', gradosTronco);
% Output: 0.901945329475723

Paso 4: Agregar a matlab_test_cases_anterior.json

{
  "id": "TC_ANT_005",
  "name": "trunk_angle",
  "description": "Ángulo de inclinación del tronco",
  "matlab_function": "Líneas 554-560 de InterfazMedicionFrenteJulio2024v2.m",
  "inputs": {
    "p_sp": [493.8188, 1182.7],
    "p_ee": [486.1345, 694.5977]
  },
  "expected_output": 0.901945329475723,
  "tolerance": 1e-10,
  "unit": "grados"
}

Paso 5: Escribir Test Unitario

def test_tc_ant_005_trunk_angle(self, matlab_test_cases):
    """
    TC_ANT_005: Ángulo de inclinación del tronco
    MATLAB: Líneas 554-560
    CRÍTICO: Algoritmo validado por estadistas
    """
    tc = next(tc for tc in matlab_test_cases['test_cases'] if tc['id'] == 'TC_ANT_005')

    p_sp = tuple(tc['inputs']['p_sp'])
    p_ee = tuple(tc['inputs']['p_ee'])

    resultado_python = trunk_angle(p_sp, p_ee)
    esperado_matlab = tc['expected_output']
    tolerancia = tc['tolerance']

    assert math.isclose(resultado_python, esperado_matlab, abs_tol=tolerancia), \
        f"Ángulo de tronco difiere: Python={resultado_python:.15f}, MATLAB={esperado_matlab:.15f}"

Paso 6: Ejecutar y Validar

python3 -m pytest tests/test_matlab_validation.py::TestMatlabValidationAnterior::test_tc_ant_005_trunk_angle -v

Criterio de éxito: Diferencia < 1e-10 (0.0000000001 grados)


📂 Estructura de Archivos

orthoposture/
├── matlab_validation/
│   └── generate_test_cases.m          # Script MATLAB para generar casos
├── tests/
│   ├── fixtures/
│   │   ├── matlab_test_cases_anterior.json      ✅ COMPLETO (8 casos)
│   │   ├── matlab_test_cases_posterior.json     ⏳ POR CREAR
│   │   ├── matlab_test_cases_perfil_der.json    ⏳ POR CREAR
│   │   └── matlab_test_cases_perfil_izq.json    ⏳ POR CREAR
│   ├── test_matlab_validation.py      # Suite de tests de validación
│   └── test_calculations.py           # Tests legacy (mantener por compatibilidad)
├── python/
│   ├── calculations.py                # Implementación Python
│   └── test_calculations.py           # Tests originales
└── docs/
    └── matlab-python-validation.md    # Esta documentación

⚠️ Reglas de Oro

1. NUNCA modificar test para hacerlo pasar

Si un test falla: - ❌ MAL: Ajustar tolerancia o cambiar valor esperado - ✅ BIEN: Revisar implementación Python vs algoritmo MATLAB línea por línea

2. Precisión mínima: 1e-10

  • Tolerancia absoluta: 1e-10 (0.0000000001)
  • Tolerancia relativa: 1e-6 solo para valores muy grandes (>1000)
  • Registrar precisión real alcanzada en cada test

3. Documentar TODAS las diferencias

Si algún algoritmo debe modificarse intencionalmente: - Documentar en docs/memory.md con fecha y justificación - Obtener aprobación de Dra. Cristina - Actualizar test case con nota explicativa

4. Un caso de prueba = Un cálculo

No mezclar múltiples cálculos en un solo test case. Cada métrica debe validarse independientemente.

5. Trazabilidad completa

Cada test case debe incluir: - Número de líneas exactas del código MATLAB - Descripción del cálculo - Referencias anatómicas/biomecánicas - Nombre del archivo fuente MATLAB


🔄 Workflow para Nueva Vista

Template para Vista Posterior

1. Crear script MATLAB

matlab_validation/generate_test_cases_posterior.m:

clear all; clc;

% Cargar puntos de muestra (vista posterior)
puntos = struct();
puntos.LobuloIzq = [X, Y];  % POR COMPLETAR
puntos.LobuloDer = [X, Y];
% ... (resto de marcadores)

% CASO 1: Ángulo calcáneo izquierdo
% Extraer de InterfazMedicionEspaldaJulio2024v2.m, líneas XXX-YYY
% Código MATLAB original aquí

% CASO 2: Distancia gemelos
% ...

% Exportar a JSON
test_cases = {test_case_1, test_case_2, ...};
json_output = struct('vista', 'posterior', 'test_cases', test_cases);
% Guardar

2. Ejecutar MATLAB

cd matlab_validation
generate_test_cases_posterior

Output: tests/fixtures/matlab_test_cases_posterior.json

3. Implementar funciones Python

python/calculations_posterior.py:

def calcaneo_angle_left(...) -> float:
    """
    MATLAB Ref: InterfazMedicionEspaldaJulio2024v2.m, líneas XXX-YYY
    """
    # Implementación idéntica a MATLAB
    pass

4. Crear tests

tests/test_matlab_validation.py (agregar clase):

class TestMatlabValidationPosterior:
    def test_tc_post_001_calcaneo_izq(self, matlab_test_cases_post):
        ...

5. Ejecutar validación

python3 -m pytest tests/test_matlab_validation.py::TestMatlabValidationPosterior -v

📊 Dashboard de Validación

Vista Tests Implementados Tests Pasando Precisión Promedio Estado
Anterior 8/8 8/8 (100%) <1e-12 ✅ COMPLETO
Posterior 0/? 0/? - ⏳ PENDIENTE
Perfil Der 0/? 0/? - ⏳ PENDIENTE
Perfil Izq 0/? 0/? - ⏳ PENDIENTE

Meta: 100% de tests pasando en las 4 vistas antes de desplegar a producción.


🚀 Próximos Pasos

Inmediatos (Sesión actual)

  1. ✅ Vista Anterior validada (8 casos)
  2. ⏳ Extraer algoritmos de Vista Posterior
  3. ⏳ Generar casos de prueba para Vista Posterior
  4. ⏳ Implementar funciones Python para Vista Posterior

Sesión 3-4

  1. Validar Vista Posterior (estimar 10-15 casos)
  2. Extraer algoritmos de Perfil Derecho
  3. Generar casos para Perfil Derecho

Sesión 5-6

  1. Validar Perfil Derecho
  2. Validar Perfil Izquierdo (verificar simetría con Derecho)
  3. Tests de integración end-to-end

Antes de Deploy

  • [ ] 100% de cálculos validados
  • [ ] Documentación completa de algoritmos
  • [ ] Tests automatizados en CI/CD
  • [ ] Aprobación de Dra. Cristina (spot check de resultados)

📝 Log de Validaciones

2025-10-06: Vista Anterior

  • Tests creados: 8
  • Tests pasando: 8
  • Precisión alcanzada: <1e-12 en todos los casos
  • Tiempo invertido: 2 horas
  • Bloqueadores: Ninguno
  • Notas: Todos los algoritmos replican exactamente MATLAB. Sin modificaciones necesarias.

🔗 Referencias

  • old/matlab/InterfazMedicionFrenteJulio2024v2 (1).m - Vista Anterior
  • old/matlab/InterfazMedicionEspaldaJulio2024v2 (1).m - Vista Posterior
  • old/matlab/InterfazMedicionPerfilDerechoJulio2024 (1).m - Perfil Derecho
  • old/matlab/InterfazMedicionPerfilIzquierdoJulio2024 (1).m - Perfil Izquierdo
  • tests/fixtures/matlab_test_cases_anterior.json - Casos de prueba Vista Anterior

Última actualización: 2025-10-06 Autor: Sistema de validación EPPA Relacionado: EPPA-016 (Validar cálculos), EPPA-027 (Tests de integración)