Skip to content

Commit 00fba84

Browse files
gestaltclaude
andcommitted
Add phylogeny validation pipeline plan to docs/plans/
Moves the Cytochrome C + 3-condition baseline comparison plan (Euclidean / generic-hyperbolic / current p-adic architecture, evaluated against independent NCBI taxonomy distance) from a local plan file into the repo, alongside EXTERNAL-VALIDATION-ROADMAP.md which it concretizes. Not started -- planning only, no code yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 25b249d commit 00fba84

2 files changed

Lines changed: 214 additions & 0 deletions

File tree

docs/plans/INDEX.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ This directory contains design proposals, roadmaps, and feature plans. These doc
1010
| [GRAPH-TOPOLOGY-VISUALIZATION-PLAN.md](GRAPH-TOPOLOGY-VISUALIZATION-PLAN.md) | Topology visualization pipeline plan |
1111
| [ALGEBRAIC-VISUALIZATION-ROADMAP.md](ALGEBRAIC-VISUALIZATION-ROADMAP.md) | Algebraic visualization roadmap |
1212
| [EXTERNAL-VALIDATION-ROADMAP.md](EXTERNAL-VALIDATION-ROADMAP.md) | Open question: does the p-adic/hyperbolic prior help on real (non-synthetic) data vs. baselines? |
13+
| [PHYLOGENY-VALIDATION-PIPELINE.md](PHYLOGENY-VALIDATION-PIPELINE.md) | Concrete plan: Cytochrome C phylogeny + 3-condition baseline comparison (Euclidean / generic-hyperbolic / p-adic), addressing the roadmap above. Not started. |
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# Pipeline de filogenia real: Cytochrome C + comparación de baselines
2+
3+
## Contexto
4+
5+
`docs/plans/EXTERNAL-VALIDATION-ROADMAP.md` deja documentado un problema de
6+
fondo: todas las métricas de "jerarquía" reportadas hasta ahora (Spearman
7+
0.8335, ARI=1.0) miden si las losses hacen lo que fueron escritas para hacer
8+
`v_3(índice)` se calcula del índice y se inyecta directamente como target,
9+
así que el resultado es casi tautológico. El roadmap identifica dos cosas que
10+
faltan para que esto sea una demostración real en vez de un ejercicio
11+
autoconsistente:
12+
13+
1. Mover a un dominio real cuya jerarquía sea conocida **de forma
14+
independiente**, no inyectada en la loss.
15+
2. Comparar contra baselines (Euclidiano, hiperbólico genérico) en la misma
16+
tarea — comparación que hoy no existe en ningún lado del repo.
17+
18+
Elegimos citocromo C como dominio porque: es el dataset clásico de reloj
19+
molecular (Fitch & Margoliash), es corto (~104-110 aa, pocas ventanas por
20+
especie), tiene cobertura real desde bacterias hasta humanos en
21+
UniProt/NCBI, y reutiliza el encoding de hidropatía que ya existe en
22+
`scripts/data/peptide_encoding.py` (ya usado por 4 scripts). El diseño de
23+
experimento acordado son **3 condiciones** (Euclidiano / Hiperbólico
24+
genérico / P-ádico actual) evaluadas post-hoc contra distancia taxonómica
25+
real — sin tocar `src/losses/` para esta primera fase (la variante D,
26+
"reapuntar" `target_radius` a la taxonomía real vía el `valuation_type`
27+
plegable que ya existe en `src/losses/combined.py:120`, queda deliberadamente
28+
fuera de alcance hasta tener resultados de las 3 primeras).
29+
30+
**Objetivo final medible:** ¿la distancia hiperbólica/p-ádica entre especies
31+
correlaciona con su distancia taxonómica real mejor de lo que lo hace un VAE
32+
euclidiano plano, en secuencias nunca usadas para fijar el target de ninguna
33+
loss?
34+
35+
---
36+
37+
## Fase 1 — Datos reales con procedencia auditable
38+
39+
**Problema a evitar:** el roadmap ya documenta que `validate_humsavar_tp53.py`
40+
usa una secuencia "tipeada a mano en un comentario". Esta vez todo dato debe
41+
venir de una fuente descargable y verificable.
42+
43+
- **Nuevo:** `scripts/data/fetch_cytochrome_c.py` — descarga secuencias reales
44+
de citocromo C vía UniProt REST API para una lista curada de ~30-40
45+
especies que cubran un rango taxonómico amplio (bacterias, hongos,
46+
plantas, invertebrados, vertebrados incluyendo humano). Guarda FASTA crudo
47+
+ manifest committeable (especie, accession, fecha de descarga, URL) en
48+
`data/cytochrome_c/manifest.csv` — el manifest se commitea aunque
49+
`data/` esté gitignoreado para las secuencias, así queda auditable sin
50+
necesidad de volver a bajar nada para revisar procedencia.
51+
- **Nuevo:** `scripts/data/fetch_taxonomy.py` — para la misma lista de
52+
especies, obtiene el linaje taxonómico completo (dominio→especie) vía la
53+
API de NCBI Taxonomy. Calcula una matriz de distancia par-a-par por
54+
"profundidad del último ancestro común en rangos Linneanos" (ej. mismo
55+
género = distancia 1, mismo dominio nada más = distancia 7) — es una
56+
ultramétrica genuina, calculable con comparación de strings de linaje, sin
57+
dependencias nuevas. Guarda `data/cytochrome_c/taxonomic_distance.npy` +
58+
`taxonomy_lineage.json`.
59+
60+
**Riesgo flageado:** ambos scripts requieren acceso a red en tiempo de
61+
ejecución (UniProt/NCBI). No se puede validar esto en modo plan; se
62+
verificará al ejecutar.
63+
64+
---
65+
66+
## Fase 2 — Alineamiento y encoding a ventanas de 9 residuos
67+
68+
Las 96+ ventanas de 9 residuos por especie que salen de un tiling naive no
69+
son comparables entre especies (posición i en la especie X no es
70+
homóloga a posición i en la especie Y sin alinear). Se necesita alinear
71+
antes de definir las ventanas.
72+
73+
- **Nuevo:** `scripts/data/align_cytochrome_c.py` — alinea cada secuencia
74+
contra una referencia (humano) con `Bio.Align.PairwiseAligner`
75+
(Biopython — confirmar que está en requirements, si no, agregarlo).
76+
Define los límites de ventana sobre las coordenadas de la referencia
77+
alineada, así "ventana k" es la misma posición estructural para todas las
78+
especies.
79+
- **Reutiliza sin cambios:** `scripts/data/peptide_encoding.py`
80+
(`encode_peptide_window`, `AA_MAP`) para mapear cada ventana alineada a un
81+
vector de 9 dígitos ternarios {-1,0,1} — el mismo encoding que ya usan
82+
`scan_human_proteome.py`, `qspr_bioactivity_scoring.py`, etc.
83+
- **Nuevo:** `scripts/data/prepare_cytochrome_c_dataset.py` — junta todo:
84+
por especie, produce ~10-12 ventanas alineadas, las convierte a índices
85+
ternarios (0-19682) vía `TERNARY.from_ternary` (mismo patrón que
86+
`seq_to_ternary_index` en `prepare_codon_data.py`), y guarda
87+
`data/cytochrome_c/indices.pt` + un mapa `window_id → (species, window_idx)`
88+
para poder reagregar embeddings por especie después del entrenamiento.
89+
El formato de salida (`torch.save(tensor(indices), ...)`) sigue el mismo
90+
patrón que `prep_human_tp53.py`.
91+
92+
---
93+
94+
## Fase 3 — Tres condiciones de entrenamiento
95+
96+
Las 3 condiciones entrenan sobre el mismo `data/cytochrome_c/indices.pt`
97+
(vía `data.indices_path`, mecanismo que **ya existe y ya soporta datasets de
98+
tamaño arbitrario** — confirmado en `src/training/bootstrap.py:92-99`,
99+
`DataAuditor.prepare_data` no asume `N=19683` en ningún punto).
100+
101+
### Condición A — Euclidiano plano (código nuevo, aislado)
102+
El motor de entrenamiento actual (`src/train.py`) trae consigo maquinaria
103+
específica de la curricula p-ádica (StateNet, Lagrangian dual ascent, LR
104+
controller, grokking detector) que no aplica a un VAE plano — forzarla
105+
sería más trabajo que escribir el loop directo.
106+
107+
- **Nuevo:** `src/models/vae_baseline.py``TernaryVAEEuclideanBaseline`:
108+
reutiliza `EncoderHead`/decoder de `src/models/vae.py` tal cual, pero
109+
se salta `DualHyperbolicProjection` por completo — reparametrización
110+
N(0,I) estándar, decodifica directo del espacio tangente.
111+
- **Nuevo:** `scripts/applications/train_euclidean_baseline.py` — loop de
112+
entrenamiento simple (reconstrucción cross-entropy + KL gaussiano
113+
estándar, Adam, sin curricula) sobre `data/cytochrome_c/indices.pt`.
114+
115+
### Condición B — Hiperbólico genérico (sin código nuevo)
116+
Arquitectura actual (`TernaryVAEV6Controllable`) + preset YAML que apaga
117+
**todas** las losses p-ádicas (`radial`, `monotonic`, `rich_hierarchy.
118+
hierarchy_weight=0`, `angular_coherence.enabled=false`,
119+
`algebraic_*.enabled=false`, `valuation_prior.enabled=false`,
120+
`geodesic`/`rank` desactivadas) dejando solo `rich_hierarchy.coverage_weight`
121+
(reconstrucción) + `hyperbolic_kl` activos. Esto es 100% config-driven —
122+
el patrón de habilitar/deshabilitar por loss ya existe en cada bloque YAML
123+
(ver `surrogate_property.enabled: false` en `v24.0_tangent_fix.yaml`).
124+
- **Nuevo:** `src/presets/cytochrome_c_B_hyperbolic_generic.yaml`
125+
126+
### Condición C — P-ádico actual, sin cambios de arquitectura
127+
Mismo `train.py` + mismo `TernaryVAEV6Controllable`, config casi idéntica a
128+
`v24.0_tangent_fix.yaml` pero apuntando `data.indices_path` al dataset de
129+
citocromo C. Esta es la prueba de "transferencia pura": el índice ternario
130+
de cada ventana de citocromo C no tiene relación causal con la especie de
131+
origen, así que si esta condición correlaciona con taxonomía real sería
132+
sorprendente (y hay que decirlo así en el resultado, sin sobre-vender).
133+
- **Nuevo:** `src/presets/cytochrome_c_C_padic.yaml`
134+
135+
**Nota de tamaño de dataset:** ~30-40 especies × ~10-12 ventanas ≈ 300-480
136+
muestras. Verificar en la ejecución si el batch size / hiperparámetros por
137+
defecto de `v24.0_tangent_fix.yaml` siguen siendo razonables a esta escala
138+
(probablemente sí, pero con menos pasos por época).
139+
140+
---
141+
142+
## Fase 4 — Evaluación: ¿recuperan filogenia real?
143+
144+
- **Nuevo:** `scripts/analysis/evaluate_phylogeny_recovery.py`:
145+
1. Para cada checkpoint (A/B/C), calcular embeddings de todas las
146+
ventanas (`model.get_mu_representations` / `get_hyperbolic_representations`,
147+
igual que `probe_foreign_genome.py`).
148+
2. Agregar las ~10-12 ventanas por especie a **un punto por especie**
149+
(media en espacio tangente vía `logmap0`/`expmap0`, ya usados en todo
150+
el codebase, para B/C; media aritmética simple para A).
151+
3. Calcular matriz de distancia par-a-par entre especies (Poincaré para
152+
B/C, Euclidiana para A) y correlacionarla (Spearman) contra
153+
`taxonomic_distance.npy` de la Fase 1.
154+
4. **Test de Mantel** (permutación, no Spearman naive) para el p-valor —
155+
los pares de una matriz de distancia no son independientes, así que
156+
un Spearman ingenuo infla la significancia. Reutilizar el patrón
157+
auto-escéptico de `scripts/validation/check_zero_count_semantics.py`
158+
(hipótesis nula explícita + baseline de permutación aleatoria).
159+
5. Bootstrap sobre especies para intervalo de confianza de la correlación
160+
de cada condición, y comparar A vs B vs C.
161+
6. Hold-out: separar ~20% de especies que nunca entren en el
162+
entrenamiento, reportar la correlación también solo sobre esas.
163+
Donde aplique, reutilizar `representation_probe_suite`/
164+
`retrieval_ablation_suite` de `scripts/analysis/project_audit.py`
165+
(`project_audit.py:341`, `:534`) en vez de reimplementar estadística —
166+
el roadmap ya señala esa máquina como la más reutilizable para esto.
167+
168+
---
169+
170+
## Fase 5 — Reporte honesto
171+
172+
Actualizar `docs/plans/EXTERNAL-VALIDATION-ROADMAP.md` con el resultado
173+
(cualquiera sea) siguiendo el mismo formato que ya usa el documento para
174+
`check_zero_count_semantics.py`: hipótesis, método, resultado numérico,
175+
veredicto explícito. Si el resultado es negativo (ninguna condición
176+
correlaciona con taxonomía real), documentarlo igual — es la clase de
177+
resultado que el roadmap pide, no uno a evitar.
178+
179+
---
180+
181+
## Archivos nuevos (resumen)
182+
183+
| Archivo | Rol |
184+
|---|---|
185+
| `scripts/data/fetch_cytochrome_c.py` | Descarga secuencias reales + manifest |
186+
| `scripts/data/fetch_taxonomy.py` | Linaje NCBI + matriz de distancia taxonómica |
187+
| `scripts/data/align_cytochrome_c.py` | Alineamiento contra referencia humana |
188+
| `scripts/data/prepare_cytochrome_c_dataset.py` | Ventanas alineadas → `indices.pt` |
189+
| `src/models/vae_baseline.py` | `TernaryVAEEuclideanBaseline` (condición A) |
190+
| `scripts/applications/train_euclidean_baseline.py` | Loop de entrenamiento condición A |
191+
| `src/presets/cytochrome_c_B_hyperbolic_generic.yaml` | Config condición B |
192+
| `src/presets/cytochrome_c_C_padic.yaml` | Config condición C |
193+
| `scripts/analysis/evaluate_phylogeny_recovery.py` | Evaluación A/B/C + Mantel + bootstrap |
194+
195+
Reutilizados sin cambios: `scripts/data/peptide_encoding.py`,
196+
`src/core/ternary.py` (`TERNARY.from_ternary`), `src/train.py`,
197+
`src/models/vae.py` (`TernaryVAEV6Controllable`), patrones de
198+
`scripts/analysis/probe_foreign_genome.py` y `project_audit.py`.
199+
200+
---
201+
202+
## Verificación
203+
204+
1. `fetch_cytochrome_c.py` / `fetch_taxonomy.py`: correr y confirmar que el
205+
manifest tiene N especies reales con accessions válidos (no placeholders).
206+
2. `prepare_cytochrome_c_dataset.py`: confirmar shape de `indices.pt` y que
207+
el mapa especie↔ventana cuadra.
208+
3. Entrenar las 3 condiciones (pocas épocas primero, smoke test, luego full
209+
run) y confirmar convergencia básica (reconstrucción no se estanca).
210+
4. Correr `evaluate_phylogeny_recovery.py` y revisar que el test de Mantel
211+
produzca un p-valor y no solo una correlación cruda.
212+
5. Confirmar que el resultado (positivo o negativo) queda escrito en el
213+
roadmap con el mismo nivel de rigor que las entradas existentes.

0 commit comments

Comments
 (0)