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, sinexpected_outputMATLAB)
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¶
- Localizar función en código
.m(líneas específicas) - Identificar variables de entrada
- Copiar algoritmo EXACTO (incluyendo estructuras
if/else, signos, etc.) - 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-6solo 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)¶
- ✅ Vista Anterior validada (8 casos)
- ⏳ Extraer algoritmos de Vista Posterior
- ⏳ Generar casos de prueba para Vista Posterior
- ⏳ Implementar funciones Python para Vista Posterior
Sesión 3-4¶
- Validar Vista Posterior (estimar 10-15 casos)
- Extraer algoritmos de Perfil Derecho
- Generar casos para Perfil Derecho
Sesión 5-6¶
- Validar Perfil Derecho
- Validar Perfil Izquierdo (verificar simetría con Derecho)
- 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 Anteriorold/matlab/InterfazMedicionEspaldaJulio2024v2 (1).m- Vista Posteriorold/matlab/InterfazMedicionPerfilDerechoJulio2024 (1).m- Perfil Derechoold/matlab/InterfazMedicionPerfilIzquierdoJulio2024 (1).m- Perfil Izquierdotests/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)