use crate::arena::PhiArena;
use crate::error::ConsciousnessError;
use crate::traits::ConsciousnessCollapse;
use crate::types::{Bipartition, PhiAlgorithm, PhiResult, TransitionMatrix};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use std::f64::consts::PI;
use std::time::Instant;
pub struct QuantumCollapseEngine {
register_size: usize,
}
impl QuantumCollapseEngine {
pub fn new(register_size: usize) -> Self {
Self { register_size }
}
}
impl Default for QuantumCollapseEngine {
fn default() -> Self {
Self {
register_size: 256,
}
}
}
impl ConsciousnessCollapse for QuantumCollapseEngine {
fn collapse_to_mip(
&self,
tpm: &TransitionMatrix,
iterations: usize,
seed: u64,
) -> Result<PhiResult, ConsciousnessError> {
let n = tpm.n;
let start = Instant::now();
let arena = PhiArena::with_capacity(n * n * 16);
let mut rng = StdRng::seed_from_u64(seed);
let total_partitions = (1u64 << n) - 2;
let reg_size = self.register_size.min(total_partitions as usize);
let mut partitions: Vec<Bipartition> = Vec::with_capacity(reg_size);
let mut seen = std::collections::HashSet::new();
while partitions.len() < reg_size {
let mask = loop {
let m = rng.gen::<u64>() & ((1u64 << n) - 1);
if m != 0 && m != (1u64 << n) - 1 && !seen.contains(&m) {
break m;
}
};
seen.insert(mask);
partitions.push(Bipartition { mask, n });
}
let losses: Vec<f64> = partitions
.iter()
.map(|p| {
let loss = super::phi::partition_information_loss_pub(tpm, 0, p, &arena);
arena.reset();
loss
})
.collect();
let inv_sqrt = 1.0 / (reg_size as f64).sqrt();
let mut amplitudes: Vec<f64> = vec![inv_sqrt; reg_size];
let optimal_iters = iterations.min(((reg_size as f64).sqrt() * PI / 4.0) as usize);
for _ in 0..optimal_iters {
let max_loss = losses.iter().copied().fold(f64::MIN, f64::max);
if max_loss < 1e-15 {
break;
}
let inv_max = 1.0 / max_loss;
for i in 0..reg_size {
let relevance = 1.0 - (losses[i] * inv_max);
let phase = PI * relevance;
amplitudes[i] *= phase.cos();
}
let mean: f64 = amplitudes.iter().sum::<f64>() / reg_size as f64;
for amp in &mut amplitudes {
*amp = 2.0 * mean - *amp;
}
}
let probs: Vec<f64> = amplitudes.iter().map(|a| a * a).collect();
let total_prob: f64 = probs.iter().sum();
let best_idx = if total_prob > 1e-15 {
let r: f64 = rng.gen::<f64>() * total_prob;
let mut cumsum = 0.0;
let mut selected = 0;
for (i, &p) in probs.iter().enumerate() {
cumsum += p;
if cumsum >= r {
selected = i;
break;
}
}
selected
} else {
losses
.iter()
.enumerate()
.min_by(|a, b| a.1.partial_cmp(b.1).unwrap())
.map(|(i, _)| i)
.unwrap_or(0)
};
Ok(PhiResult {
phi: losses[best_idx],
mip: partitions[best_idx].clone(),
partitions_evaluated: reg_size as u64,
total_partitions,
algorithm: PhiAlgorithm::Collapse,
elapsed: start.elapsed(),
convergence: vec![losses[best_idx]],
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn simple_tpm() -> TransitionMatrix {
#[rustfmt::skip]
let data = vec![
0.5, 0.25, 0.25, 0.0,
0.5, 0.25, 0.25, 0.0,
0.5, 0.25, 0.25, 0.0,
0.0, 0.0, 0.0, 1.0,
];
TransitionMatrix::new(4, data)
}
#[test]
fn collapse_finds_partition() {
let tpm = simple_tpm();
let engine = QuantumCollapseEngine::new(32);
let result = engine.collapse_to_mip(&tpm, 10, 42).unwrap();
assert!(result.phi >= 0.0);
assert!(result.mip.is_valid());
}
#[test]
fn collapse_deterministic_with_seed() {
let tpm = simple_tpm();
let engine = QuantumCollapseEngine::new(32);
let r1 = engine.collapse_to_mip(&tpm, 10, 42).unwrap();
let r2 = engine.collapse_to_mip(&tpm, 10, 42).unwrap();
assert_eq!(r1.mip, r2.mip);
}
}