#[derive(Debug, Clone)]
pub struct RvDnaPattern {
pub id: u64,
pub health_embedding: [f32; 64],
pub polygenic_risk: f32,
pub biological_age: f32,
pub chronological_age: f32,
pub sample_hash: [u8; 32],
pub neuro_profile: NeurotransmitterProfile,
}
#[derive(Debug, Clone, Default)]
pub struct NeurotransmitterProfile {
pub dopamine: f32,
pub serotonin: f32,
pub gaba_glutamate_ratio: f32,
pub plasticity_score: f32,
pub circadian_regulation: f32,
}
impl NeurotransmitterProfile {
pub fn excitability_score(&self) -> f32 {
(self.dopamine * 0.3
+ self.serotonin * 0.2
+ self.gaba_glutamate_ratio * 0.2
+ self.plasticity_score * 0.3)
.clamp(0.0, 1.0)
}
pub fn circadian_phase_rad(&self) -> f32 {
self.circadian_regulation * 2.0 * std::f32::consts::PI
}
}
pub struct HorvathClock {
pub intercept: f64,
adult_age_transform: f64,
}
impl HorvathClock {
pub fn new() -> Self {
Self {
intercept: 0.696,
adult_age_transform: 20.0,
}
}
pub fn predict_age(&self, methylation_proxy: &[f32]) -> f32 {
if methylation_proxy.is_empty() {
return 30.0;
}
let signal: f64 = methylation_proxy
.iter()
.enumerate()
.map(|(i, &m)| {
let w = if i % 2 == 0 { 1.5 } else { -0.8 };
w * m as f64
})
.sum::<f64>()
/ methylation_proxy.len() as f64;
let transformed = self.intercept + signal;
if transformed < 0.0 {
(self.adult_age_transform * 2.0_f64.powf(transformed) - 1.0) as f32
} else {
(self.adult_age_transform * (transformed + 1.0)) as f32
}
}
pub fn age_acceleration(&self, methylation: &[f32], chronological_age: f32) -> f32 {
let bio_age = self.predict_age(methylation);
bio_age - chronological_age
}
}
impl Default for HorvathClock {
fn default() -> Self {
Self::new()
}
}
pub struct PharmacogenomicWeights {
#[allow(dead_code)]
clock: HorvathClock,
}
impl PharmacogenomicWeights {
pub fn new() -> Self {
Self {
clock: HorvathClock::new(),
}
}
pub fn phi_weight(&self, neuro: &NeurotransmitterProfile) -> f64 {
let excit = neuro.excitability_score() as f64;
let plastic = neuro.plasticity_score as f64;
(1.0 + 3.0 * excit * plastic).min(5.0)
}
pub fn connection_weight_scale(&self, neuro: &NeurotransmitterProfile) -> f32 {
let da_effect = 1.0 + 0.5 * neuro.dopamine; let gaba_effect = 1.0 - 0.3 * neuro.gaba_glutamate_ratio; (da_effect * gaba_effect).clamp(0.3, 2.5)
}
pub fn memory_decay_rate(&self, bio_age: f32) -> f64 {
1.0 / (1.0 + (-0.1 * (bio_age as f64 - 40.0)).exp())
}
}
impl Default for PharmacogenomicWeights {
fn default() -> Self {
Self::new()
}
}
pub struct GenomicPatternStore {
patterns: Vec<RvDnaPattern>,
weights: PharmacogenomicWeights,
}
#[derive(Debug)]
pub struct GenomicSearchResult {
pub id: u64,
pub similarity: f32,
pub phi_weight: f64,
pub weighted_score: f64,
}
impl GenomicPatternStore {
pub fn new() -> Self {
Self {
patterns: Vec::new(),
weights: PharmacogenomicWeights::new(),
}
}
pub fn insert(&mut self, pattern: RvDnaPattern) {
self.patterns.push(pattern);
}
fn cosine_similarity(a: &[f32; 64], b: &[f32; 64]) -> f32 {
let dot: f32 = a.iter().zip(b.iter()).map(|(x, y)| x * y).sum();
let na: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt().max(1e-8);
let nb: f32 = b.iter().map(|x| x * x).sum::<f32>().sqrt().max(1e-8);
dot / (na * nb)
}
pub fn search(&self, query: &RvDnaPattern, k: usize) -> Vec<GenomicSearchResult> {
let mut results: Vec<GenomicSearchResult> = self
.patterns
.iter()
.map(|p| {
let sim = Self::cosine_similarity(&query.health_embedding, &p.health_embedding);
let phi_w = self.weights.phi_weight(&p.neuro_profile);
GenomicSearchResult {
id: p.id,
similarity: sim,
phi_weight: phi_w,
weighted_score: sim as f64 * phi_w,
}
})
.collect();
results.sort_unstable_by(|a, b| {
b.weighted_score
.partial_cmp(&a.weighted_score)
.unwrap_or(std::cmp::Ordering::Equal)
});
results.truncate(k);
results
}
pub fn len(&self) -> usize {
self.patterns.len()
}
}
impl Default for GenomicPatternStore {
fn default() -> Self {
Self::new()
}
}
pub fn synthetic_rvdna_pattern(id: u64, seed: u64) -> RvDnaPattern {
let mut health = [0.0f32; 64];
let mut s = seed.wrapping_mul(0x9e3779b97f4a7c15);
for h in health.iter_mut() {
s = s
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
*h = (s >> 33) as f32 / (u32::MAX as f32);
}
let neuro = NeurotransmitterProfile {
dopamine: (seed as f32 * 0.1) % 1.0,
serotonin: ((seed + 1) as f32 * 0.15) % 1.0,
gaba_glutamate_ratio: 0.5,
plasticity_score: ((seed + 2) as f32 * 0.07) % 1.0,
circadian_regulation: ((seed + 3) as f32 * 0.13) % 1.0,
};
RvDnaPattern {
id,
health_embedding: health,
polygenic_risk: (seed as f32 * 0.003) % 1.0,
biological_age: 20.0 + (seed as f32 * 0.5) % 40.0,
chronological_age: 25.0 + (seed as f32 * 0.4) % 35.0,
sample_hash: [0u8; 32],
neuro_profile: neuro,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_horvath_clock_adult_age() {
let clock = HorvathClock::new();
let methylation = vec![0.5f32; 10];
let age = clock.predict_age(&methylation);
assert!(
age > 0.0 && age < 120.0,
"Biological age should be in [0, 120]: {}",
age
);
}
#[test]
fn test_phi_weight_scales_with_excitability() {
let weights = PharmacogenomicWeights::new();
let low_neuro = NeurotransmitterProfile {
dopamine: 0.1,
serotonin: 0.1,
gaba_glutamate_ratio: 0.1,
plasticity_score: 0.1,
circadian_regulation: 0.5,
};
let high_neuro = NeurotransmitterProfile {
dopamine: 0.9,
serotonin: 0.8,
gaba_glutamate_ratio: 0.5,
plasticity_score: 0.9,
circadian_regulation: 0.5,
};
let low_phi = weights.phi_weight(&low_neuro);
let high_phi = weights.phi_weight(&high_neuro);
assert!(
high_phi > low_phi,
"High excitability should yield higher Φ weight"
);
}
#[test]
fn test_genomic_store_search() {
let mut store = GenomicPatternStore::new();
for i in 0..10u64 {
store.insert(synthetic_rvdna_pattern(i, i * 13));
}
let query = synthetic_rvdna_pattern(0, 0);
let results = store.search(&query, 3);
assert!(!results.is_empty());
assert!(
results[0].weighted_score >= results.last().map(|r| r.weighted_score).unwrap_or(0.0)
);
}
#[test]
fn test_neuro_circadian_phase() {
let neuro = NeurotransmitterProfile {
circadian_regulation: 0.5,
..Default::default()
};
let phase = neuro.circadian_phase_rad();
assert!(phase >= 0.0 && phase <= 2.0 * std::f32::consts::PI);
}
}