use crate::error::{ConsciousnessError, ValidationError};
use crate::simd::entropy;
use crate::types::{ComputeBudget, TransitionMatrix};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use std::time::Instant;
pub fn randomized_svd(tpm: &TransitionMatrix, k: usize, oversampling: usize, seed: u64) -> Vec<f64> {
let n = tpm.n;
let rank = (k + oversampling).min(n);
let mut rng = StdRng::seed_from_u64(seed);
let mut omega = vec![0.0f64; n * rank];
for val in &mut omega {
let u1: f64 = rng.gen::<f64>().max(1e-15);
let u2: f64 = rng.gen::<f64>();
*val = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
}
let mut y = vec![0.0f64; n * rank];
for i in 0..n {
for j in 0..rank {
let mut sum = 0.0;
for l in 0..n {
sum += tpm.get(i, l) * omega[l * rank + j];
}
y[i * rank + j] = sum;
}
}
let mut q = y.clone();
let mut r = vec![0.0f64; rank * rank];
for j in 0..rank {
let mut norm = 0.0;
for i in 0..n {
norm += q[i * rank + j] * q[i * rank + j];
}
norm = norm.sqrt();
r[j * rank + j] = norm;
if norm > 1e-15 {
let inv_norm = 1.0 / norm;
for i in 0..n {
q[i * rank + j] *= inv_norm;
}
}
for jj in (j + 1)..rank {
let mut dot = 0.0;
for i in 0..n {
dot += q[i * rank + j] * q[i * rank + jj];
}
r[j * rank + jj] = dot;
for i in 0..n {
q[i * rank + jj] -= dot * q[i * rank + j];
}
}
}
let mut b = vec![0.0f64; rank * n];
for i in 0..rank {
for j in 0..n {
let mut sum = 0.0;
for l in 0..n {
sum += q[l * rank + i] * tpm.get(l, j);
}
b[i * n + j] = sum;
}
}
let mut bbt = vec![0.0f64; rank * rank];
for i in 0..rank {
for j in 0..rank {
let mut sum = 0.0;
for l in 0..n {
sum += b[i * n + l] * b[j * n + l];
}
bbt[i * rank + j] = sum;
}
}
let mut eigenvalues = Vec::with_capacity(k);
let mut matrix = bbt;
for _ in 0..k {
let ev = largest_eigenvalue_power(&matrix, rank, 200, &mut rng);
eigenvalues.push(ev.sqrt().max(0.0));
let v = dominant_eigenvector(&matrix, rank, 200, &mut rng);
for i in 0..rank {
for j in 0..rank {
matrix[i * rank + j] -= ev * v[i] * v[j];
}
}
}
eigenvalues.sort_by(|a, b| b.partial_cmp(a).unwrap());
eigenvalues
}
fn largest_eigenvalue_power(matrix: &[f64], n: usize, max_iter: usize, rng: &mut StdRng) -> f64 {
let mut v: Vec<f64> = (0..n).map(|_| rng.gen::<f64>() - 0.5).collect();
let norm: f64 = v.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm > 1e-15 {
for vi in &mut v {
*vi /= norm;
}
}
let mut eigenvalue = 0.0;
let mut w = vec![0.0f64; n];
for _ in 0..max_iter {
for i in 0..n {
let mut sum = 0.0;
for j in 0..n {
sum += matrix[i * n + j] * v[j];
}
w[i] = sum;
}
let mut dot = 0.0;
for i in 0..n {
dot += v[i] * w[i];
}
eigenvalue = dot;
let norm: f64 = w.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm < 1e-15 {
break;
}
let inv_norm = 1.0 / norm;
for i in 0..n {
v[i] = w[i] * inv_norm;
}
}
eigenvalue.max(0.0)
}
fn dominant_eigenvector(matrix: &[f64], n: usize, max_iter: usize, rng: &mut StdRng) -> Vec<f64> {
let mut v: Vec<f64> = (0..n).map(|_| rng.gen::<f64>() - 0.5).collect();
let norm: f64 = v.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm > 1e-15 {
for vi in &mut v {
*vi /= norm;
}
}
let mut w = vec![0.0f64; n];
for _ in 0..max_iter {
for i in 0..n {
let mut sum = 0.0;
for j in 0..n {
sum += matrix[i * n + j] * v[j];
}
w[i] = sum;
}
let norm: f64 = w.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm < 1e-15 {
break;
}
let inv_norm = 1.0 / norm;
for i in 0..n {
v[i] = w[i] * inv_norm;
}
}
v
}
pub struct RsvdEmergenceEngine {
pub k: usize,
pub oversampling: usize,
pub seed: u64,
}
impl RsvdEmergenceEngine {
pub fn new(k: usize, oversampling: usize, seed: u64) -> Self {
Self { k, oversampling, seed }
}
}
impl Default for RsvdEmergenceEngine {
fn default() -> Self {
Self {
k: 10,
oversampling: 5,
seed: 42,
}
}
}
impl RsvdEmergenceEngine {
pub fn compute(
&self,
tpm: &TransitionMatrix,
_budget: &ComputeBudget,
) -> Result<RsvdEmergenceResult, ConsciousnessError> {
let start = Instant::now();
let n = tpm.n;
if n < 2 {
return Err(ValidationError::EmptySystem.into());
}
let k = self.k.min(n);
let singular_values = randomized_svd(tpm, k, self.oversampling, self.seed);
let max_sv = singular_values.first().copied().unwrap_or(0.0);
let threshold = max_sv * 1e-6;
let effective_rank = singular_values.iter().filter(|&&s| s > threshold).count();
let sv_sum: f64 = singular_values.iter().sum();
let spectral_entropy = if sv_sum > 1e-15 {
let normalized: Vec<f64> = singular_values.iter().map(|&s| s / sv_sum).collect();
entropy(&normalized)
} else {
0.0
};
let max_spectral_entropy = (k as f64).ln();
let emergence_index = if max_spectral_entropy > 1e-15 {
1.0 - (spectral_entropy / max_spectral_entropy)
} else {
0.0
};
let reversibility = if singular_values.len() >= 2 && max_sv > 1e-15 {
singular_values.last().copied().unwrap_or(0.0) / max_sv
} else {
0.0
};
Ok(RsvdEmergenceResult {
singular_values,
effective_rank,
spectral_entropy,
emergence_index,
reversibility,
elapsed: start.elapsed(),
})
}
}
#[derive(Debug, Clone)]
pub struct RsvdEmergenceResult {
pub singular_values: Vec<f64>,
pub effective_rank: usize,
pub spectral_entropy: f64,
pub emergence_index: f64,
pub reversibility: f64,
pub elapsed: std::time::Duration,
}
#[cfg(test)]
mod tests {
use super::*;
fn identity_tpm(n: usize) -> TransitionMatrix {
TransitionMatrix::identity(n)
}
fn uniform_tpm(n: usize) -> TransitionMatrix {
let val = 1.0 / n as f64;
TransitionMatrix::new(n, vec![val; n * n])
}
#[test]
fn rsvd_identity_singular_values() {
let tpm = identity_tpm(4);
let svs = randomized_svd(&tpm, 4, 2, 42);
for sv in &svs {
assert!((*sv - 1.0).abs() < 0.1, "identity sv should be ≈ 1, got {sv}");
}
}
#[test]
fn rsvd_uniform_low_rank() {
let tpm = uniform_tpm(4);
let svs = randomized_svd(&tpm, 4, 2, 42);
assert!(svs[0] > 0.1, "first sv should be significant");
for sv in &svs[1..] {
assert!(*sv < 0.2, "remaining svs should be small, got {sv}");
}
}
#[test]
fn rsvd_emergence_identity() {
let tpm = identity_tpm(4);
let engine = RsvdEmergenceEngine::default();
let budget = ComputeBudget::fast();
let result = engine.compute(&tpm, &budget).unwrap();
assert!(result.emergence_index < 0.5, "identity should have low emergence, got {}", result.emergence_index);
}
#[test]
fn rsvd_emergence_uniform() {
let tpm = uniform_tpm(4);
let engine = RsvdEmergenceEngine::default();
let budget = ComputeBudget::fast();
let result = engine.compute(&tpm, &budget).unwrap();
assert!(result.effective_rank <= 2, "uniform should have low effective rank, got {}", result.effective_rank);
}
#[test]
fn rsvd_reversibility_bounded() {
let tpm = identity_tpm(8);
let engine = RsvdEmergenceEngine::new(5, 3, 42);
let budget = ComputeBudget::fast();
let result = engine.compute(&tpm, &budget).unwrap();
assert!(result.reversibility >= 0.0 && result.reversibility <= 1.0);
}
}