ruvector_consciousness/
collapse.rs1use crate::arena::PhiArena;
12use crate::error::ConsciousnessError;
13use crate::traits::ConsciousnessCollapse;
14use crate::types::{Bipartition, PhiAlgorithm, PhiResult, TransitionMatrix};
15
16use rand::rngs::StdRng;
17use rand::{Rng, SeedableRng};
18use std::f64::consts::PI;
19use std::time::Instant;
20
21pub struct QuantumCollapseEngine {
31 register_size: usize,
33}
34
35impl QuantumCollapseEngine {
36 pub fn new(register_size: usize) -> Self {
37 Self { register_size }
38 }
39}
40
41impl Default for QuantumCollapseEngine {
42 fn default() -> Self {
43 Self {
44 register_size: 256,
45 }
46 }
47}
48
49impl ConsciousnessCollapse for QuantumCollapseEngine {
50 fn collapse_to_mip(
51 &self,
52 tpm: &TransitionMatrix,
53 iterations: usize,
54 seed: u64,
55 ) -> Result<PhiResult, ConsciousnessError> {
56 let n = tpm.n;
57 let start = Instant::now();
58 let arena = PhiArena::with_capacity(n * n * 16);
59
60 let mut rng = StdRng::seed_from_u64(seed);
61 let total_partitions = (1u64 << n) - 2;
62
63 let reg_size = self.register_size.min(total_partitions as usize);
65 let mut partitions: Vec<Bipartition> = Vec::with_capacity(reg_size);
66 let mut seen = std::collections::HashSet::new();
67
68 while partitions.len() < reg_size {
69 let mask = loop {
70 let m = rng.gen::<u64>() & ((1u64 << n) - 1);
71 if m != 0 && m != (1u64 << n) - 1 && !seen.contains(&m) {
72 break m;
73 }
74 };
75 seen.insert(mask);
76 partitions.push(Bipartition { mask, n });
77 }
78
79 let losses: Vec<f64> = partitions
81 .iter()
82 .map(|p| {
83 let loss = super::phi::partition_information_loss_pub(tpm, 0, p, &arena);
84 arena.reset();
85 loss
86 })
87 .collect();
88
89 let inv_sqrt = 1.0 / (reg_size as f64).sqrt();
91 let mut amplitudes: Vec<f64> = vec![inv_sqrt; reg_size];
92
93 let optimal_iters = iterations.min(((reg_size as f64).sqrt() * PI / 4.0) as usize);
95
96 for _ in 0..optimal_iters {
97 let max_loss = losses.iter().copied().fold(f64::MIN, f64::max);
100 if max_loss < 1e-15 {
101 break;
102 }
103 let inv_max = 1.0 / max_loss;
104
105 for i in 0..reg_size {
106 let relevance = 1.0 - (losses[i] * inv_max);
107 let phase = PI * relevance;
108 amplitudes[i] *= phase.cos();
109 }
110
111 let mean: f64 = amplitudes.iter().sum::<f64>() / reg_size as f64;
113 for amp in &mut amplitudes {
114 *amp = 2.0 * mean - *amp;
115 }
116 }
117
118 let probs: Vec<f64> = amplitudes.iter().map(|a| a * a).collect();
120 let total_prob: f64 = probs.iter().sum();
121
122 let best_idx = if total_prob > 1e-15 {
123 let r: f64 = rng.gen::<f64>() * total_prob;
125 let mut cumsum = 0.0;
126 let mut selected = 0;
127 for (i, &p) in probs.iter().enumerate() {
128 cumsum += p;
129 if cumsum >= r {
130 selected = i;
131 break;
132 }
133 }
134 selected
135 } else {
136 losses
138 .iter()
139 .enumerate()
140 .min_by(|a, b| a.1.partial_cmp(b.1).unwrap())
141 .map(|(i, _)| i)
142 .unwrap_or(0)
143 };
144
145 Ok(PhiResult {
146 phi: losses[best_idx],
147 mip: partitions[best_idx].clone(),
148 partitions_evaluated: reg_size as u64,
149 total_partitions,
150 algorithm: PhiAlgorithm::Collapse,
151 elapsed: start.elapsed(),
152 convergence: vec![losses[best_idx]],
153 })
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 fn simple_tpm() -> TransitionMatrix {
162 #[rustfmt::skip]
163 let data = vec![
164 0.5, 0.25, 0.25, 0.0,
165 0.5, 0.25, 0.25, 0.0,
166 0.5, 0.25, 0.25, 0.0,
167 0.0, 0.0, 0.0, 1.0,
168 ];
169 TransitionMatrix::new(4, data)
170 }
171
172 #[test]
173 fn collapse_finds_partition() {
174 let tpm = simple_tpm();
175 let engine = QuantumCollapseEngine::new(32);
176 let result = engine.collapse_to_mip(&tpm, 10, 42).unwrap();
177 assert!(result.phi >= 0.0);
178 assert!(result.mip.is_valid());
179 }
180
181 #[test]
182 fn collapse_deterministic_with_seed() {
183 let tpm = simple_tpm();
184 let engine = QuantumCollapseEngine::new(32);
185 let r1 = engine.collapse_to_mip(&tpm, 10, 42).unwrap();
186 let r2 = engine.collapse_to_mip(&tpm, 10, 42).unwrap();
187 assert_eq!(r1.mip, r2.mip);
188 }
189}