exo_temporal/
quantum_decay.rs1use std::time::{Duration, Instant};
12
13#[derive(Debug, Clone)]
15pub struct PatternDecoherence {
16 pub id: u64,
18 pub t1: Duration,
20 pub t2: Duration,
22 pub created_at: Instant,
24 pub last_retrieved: Instant,
26 pub phi: f64,
28 pub retrieval_count: u32,
30}
31
32impl PatternDecoherence {
33 pub fn new(id: u64, phi: f64) -> Self {
34 let now = Instant::now();
35 let phi_factor = (1.0 + phi * 0.5).min(10.0); let t1 = Duration::from_millis((60_000.0 * phi_factor) as u64);
39 let t2 = Duration::from_millis((30_000.0 * phi_factor) as u64);
40 Self {
41 id,
42 t1,
43 t2,
44 created_at: now,
45 last_retrieved: now,
46 phi,
47 retrieval_count: 0,
48 }
49 }
50
51 pub fn refresh(&mut self) {
53 self.last_retrieved = Instant::now();
54 self.retrieval_count += 1;
55 self.t2 = Duration::from_millis(
57 (self.t2.as_millis() as f64 * 1.1).min(self.t1.as_millis() as f64) as u64,
58 );
59 }
60
61 pub fn coherence_amplitude(&self) -> f64 {
63 let elapsed = self.last_retrieved.elapsed().as_millis() as f64;
64 let t2_ms = self.t2.as_millis() as f64;
65 (-elapsed / t2_ms).exp().max(0.0)
66 }
67
68 pub fn existence_probability(&self) -> f64 {
70 let elapsed = self.created_at.elapsed().as_millis() as f64;
71 let t1_ms = self.t1.as_millis() as f64;
72 (-elapsed / t1_ms).exp().max(0.0)
73 }
74
75 pub fn decoherence_score(&self) -> f64 {
78 self.coherence_amplitude() * self.existence_probability()
79 }
80
81 pub fn should_evict(&self, threshold: f64) -> bool {
83 self.decoherence_score() < threshold
84 }
85}
86
87pub struct QuantumDecayPool {
89 pub patterns: Vec<PatternDecoherence>,
90 pub eviction_threshold: f64,
92 pub max_size: usize,
94}
95
96impl QuantumDecayPool {
97 pub fn new(max_size: usize) -> Self {
98 Self {
99 patterns: Vec::with_capacity(max_size),
100 eviction_threshold: 0.1,
101 max_size,
102 }
103 }
104
105 pub fn register(&mut self, id: u64, phi: f64) {
107 if self.patterns.len() >= self.max_size {
108 self.evict_weakest();
109 }
110 self.patterns.push(PatternDecoherence::new(id, phi));
111 }
112
113 pub fn on_retrieve(&mut self, id: u64) {
115 if let Some(p) = self.patterns.iter_mut().find(|p| p.id == id) {
116 p.refresh();
117 }
118 }
119
120 pub fn weighted_score(&self, id: u64, base_score: f64) -> f64 {
122 self.patterns
123 .iter()
124 .find(|p| p.id == id)
125 .map(|p| base_score * (0.3 + 0.7 * p.decoherence_score()))
126 .unwrap_or(base_score * 0.5) }
128
129 pub fn evict_decoherent(&mut self) -> usize {
131 let threshold = self.eviction_threshold;
132 let before = self.patterns.len();
133 self.patterns.retain(|p| !p.should_evict(threshold));
134 before - self.patterns.len()
135 }
136
137 fn evict_weakest(&mut self) {
139 if let Some(idx) = self
140 .patterns
141 .iter()
142 .enumerate()
143 .min_by(|a, b| {
144 a.1.decoherence_score()
145 .partial_cmp(&b.1.decoherence_score())
146 .unwrap_or(std::cmp::Ordering::Equal)
147 })
148 .map(|(i, _)| i)
149 {
150 self.patterns.remove(idx);
151 }
152 }
153
154 pub fn len(&self) -> usize {
155 self.patterns.len()
156 }
157 pub fn is_empty(&self) -> bool {
158 self.patterns.is_empty()
159 }
160
161 pub fn stats(&self) -> DecayPoolStats {
163 if self.patterns.is_empty() {
164 return DecayPoolStats::default();
165 }
166 let scores: Vec<f64> = self
167 .patterns
168 .iter()
169 .map(|p| p.decoherence_score())
170 .collect();
171 let mean = scores.iter().sum::<f64>() / scores.len() as f64;
172 let min = scores.iter().cloned().fold(f64::INFINITY, f64::min);
173 let max = scores.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
174 DecayPoolStats {
175 count: self.patterns.len(),
176 mean_score: mean,
177 min_score: min,
178 max_score: max,
179 }
180 }
181}
182
183#[derive(Debug, Default)]
184pub struct DecayPoolStats {
185 pub count: usize,
186 pub mean_score: f64,
187 pub min_score: f64,
188 pub max_score: f64,
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_phi_extends_coherence_time() {
197 let low_phi = PatternDecoherence::new(0, 0.1);
198 let high_phi = PatternDecoherence::new(1, 5.0);
199 assert!(high_phi.t1 > low_phi.t1, "High Φ should extend T1");
201 assert!(high_phi.t2 > low_phi.t2, "High Φ should extend T2");
202 }
203
204 #[test]
205 fn test_t2_less_than_t1() {
206 let pattern = PatternDecoherence::new(0, 1.0);
207 assert!(
208 pattern.t2 <= pattern.t1,
209 "T2 must never exceed T1 (physical constraint)"
210 );
211 }
212
213 #[test]
214 fn test_retrieval_refreshes_coherence() {
215 let mut pattern = PatternDecoherence::new(0, 1.0);
216 let initial_t2 = pattern.t2;
217 pattern.refresh();
218 assert!(pattern.t2 >= initial_t2, "Retrieval should not decrease T2");
219 assert_eq!(pattern.retrieval_count, 1);
220 }
221
222 #[test]
223 fn test_pool_evicts_decoherent() {
224 let mut pool = QuantumDecayPool::new(100);
225 let mut fast_decoh = PatternDecoherence::new(99, 0.0001);
227 fast_decoh.t1 = Duration::from_micros(1);
228 fast_decoh.t2 = Duration::from_micros(1);
229 pool.patterns.push(fast_decoh);
230 pool.register(1, 10.0);
232 std::thread::sleep(Duration::from_millis(5));
233 let evicted = pool.evict_decoherent();
234 assert!(evicted > 0, "Fast-decoherent pattern should be evicted");
235 assert!(
236 pool.patterns.iter().any(|p| p.id == 1),
237 "High-Φ pattern should survive"
238 );
239 }
240
241 #[test]
242 fn test_decoherence_weighted_score() {
243 let mut pool = QuantumDecayPool::new(10);
244 pool.register(5, 2.0);
245 let weighted = pool.weighted_score(5, 1.0);
246 assert!(
248 weighted > 0.0 && weighted <= 1.0,
249 "Weighted score should be in (0,1]"
250 );
251 }
252}