refine(rvdna): calibrate biomarker weights and ranges from Genetic Lifehacks clinical data

Evidence-based adjustments from geneticlifehacks.com research articles:

- MTHFR C677T (rs1801133): het weight 0.30→0.35 to match documented
  40% enzyme activity decrease
- MTHFR A1298C (rs1801131): het 0.15→0.10, hom_alt 0.35→0.25 to
  match documented ~20% enzyme decrease
- Homocysteine reference range: 4-12→5-15 μmol/L (clinical consensus),
  critical_high 50→30 (moderate hyperhomocysteinemia threshold)
- Add MTHFR A1298C × COMT interaction (1.25x Neurological): A1298C
  homozygous + COMT slow = amplified depression risk
- Add DRD2/ANKK1 × COMT interaction (1.2x Neurological): rs1800497 ×
  Val158Met working memory interaction
- Guard vector encoding with .take(4) so expanded interaction table
  (now 6 entries) doesn't overflow dims 56-59

Sources:
- geneticlifehacks.com/mthfr/ (enzyme activity percentages)
- geneticlifehacks.com/mthfr-c677t/ (MTHFR-COMT depression data)
- geneticlifehacks.com/understanding-homocysteine-levels/ (ref ranges)
- geneticlifehacks.com/dopamine-receptor-genes/ (DRD2×COMT interaction)

All 48 tests pass (33 unit + 15 integration), benchmark compiles.

https://claude.ai/code/session_014FpaYVohmyLH5dcBZTgmSY
This commit is contained in:
Claude 2026-02-22 05:49:22 +00:00
parent c96d226357
commit 22dc2686fa
No known key found for this signature in database

View file

@ -38,7 +38,7 @@ static REFERENCES: &[BiomarkerReference] = &[
BiomarkerReference { name: "Triglycerides", unit: "mg/dL", normal_low: 0.0, normal_high: 150.0, critical_low: None, critical_high: Some(500.0), category: "Lipid" },
BiomarkerReference { name: "Fasting Glucose", unit: "mg/dL", normal_low: 70.0, normal_high: 100.0, critical_low: Some(50.0), critical_high: Some(250.0), category: "Metabolic" },
BiomarkerReference { name: "HbA1c", unit: "%", normal_low: 4.0, normal_high: 5.7, critical_low: None, critical_high: Some(9.0), category: "Metabolic" },
BiomarkerReference { name: "Homocysteine", unit: "umol/L", normal_low: 4.0, normal_high: 12.0, critical_low: None, critical_high: Some(50.0), category: "Metabolic" },
BiomarkerReference { name: "Homocysteine", unit: "umol/L", normal_low: 5.0, normal_high: 15.0, critical_low: None, critical_high: Some(30.0), category: "Metabolic" },
BiomarkerReference { name: "Vitamin D", unit: "ng/mL", normal_low: 30.0, normal_high: 80.0, critical_low: Some(10.0), critical_high: Some(150.0), category: "Nutritional" },
BiomarkerReference { name: "CRP", unit: "mg/L", normal_low: 0.0, normal_high: 3.0, critical_low: None, critical_high: Some(10.0), category: "Inflammatory" },
BiomarkerReference { name: "TSH", unit: "mIU/L", normal_low: 0.4, normal_high: 4.0, critical_low: Some(0.1), critical_high: Some(10.0), category: "Thyroid" },
@ -108,8 +108,8 @@ static SNP_WEIGHTS: &[(&str, &str, f64, f64, f64)] = &[
("rs80357906","Cancer Risk", 0.0, 0.7, 0.95),
("rs28897696","Cancer Risk", 0.0, 0.3, 0.6),
("rs11571833","Cancer Risk", 0.0, 0.25, 0.5),
("rs1801133", "Metabolism", 0.0, 0.3, 0.7),
("rs1801131", "Metabolism", 0.0, 0.15, 0.35),
("rs1801133", "Metabolism", 0.0, 0.35, 0.7), // C677T: het=40% enzyme decrease (geneticlifehacks)
("rs1801131", "Metabolism", 0.0, 0.10, 0.25), // A1298C: hom_alt=~20% decrease (geneticlifehacks)
("rs4680", "Neurological", 0.0, 0.2, 0.45),
("rs1799971", "Neurological", 0.0, 0.2, 0.4),
("rs762551", "Metabolism", 0.0, 0.15, 0.35),
@ -148,6 +148,8 @@ static INTERACTIONS: &[Interaction] = &[
Interaction { rsid_a: "rs1801133", rsid_b: "rs1801131", modifier: 1.3, category: "Metabolism" },
Interaction { rsid_a: "rs429358", rsid_b: "rs1042522", modifier: 1.2, category: "Cancer Risk" },
Interaction { rsid_a: "rs80357906",rsid_b: "rs1042522", modifier: 1.5, category: "Cancer Risk" },
Interaction { rsid_a: "rs1801131", rsid_b: "rs4680", modifier: 1.25, category: "Neurological" }, // A1298C×COMT depression (geneticlifehacks)
Interaction { rsid_a: "rs1800497", rsid_b: "rs4680", modifier: 1.2, category: "Neurological" }, // DRD2×COMT working memory (geneticlifehacks)
];
fn snp_idx(rsid: &str) -> Option<usize> {
@ -250,7 +252,9 @@ fn encode_profile_vector_with_genotypes(profile: &BiomarkerProfile, genotypes: &
}
v[55] = profile.global_risk_score as f32;
for (j, inter) in INTERACTIONS.iter().enumerate() {
// Encode first 4 interactions in dims 56-59; additional interactions
// affect category scores (dims 51-54) but don't need dedicated dims.
for (j, inter) in INTERACTIONS.iter().take(4).enumerate() {
let m = interaction_mod(genotypes, inter);
v[56 + j] = if m > 1.0 { (m - 1.0) as f32 } else { 0.0 };
}