git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
497 lines
14 KiB
Rust
497 lines
14 KiB
Rust
//! SIMD-Optimized Operations for Meta-Simulation
|
|
//!
|
|
//! Provides vectorized operations for:
|
|
//! 1. Matrix-vector multiplication (eigenvalue computation)
|
|
//! 2. Batch entropy calculations
|
|
//! 3. Parallel Φ evaluation
|
|
//! 4. Counterfactual simulation branching
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
use std::arch::x86_64::*;
|
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
use std::arch::aarch64::*;
|
|
|
|
/// SIMD-optimized matrix-vector multiply: y = A * x
|
|
/// Used in power iteration for eigenvalue computation
|
|
#[inline]
|
|
pub fn simd_matvec_multiply(matrix: &[Vec<f64>], vec: &[f64], result: &mut [f64]) {
|
|
let n = matrix.len();
|
|
assert_eq!(vec.len(), n);
|
|
assert_eq!(result.len(), n);
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
unsafe {
|
|
simd_matvec_multiply_avx2(matrix, vec, result)
|
|
}
|
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
unsafe {
|
|
simd_matvec_multiply_neon(matrix, vec, result)
|
|
}
|
|
|
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
|
{
|
|
simd_matvec_multiply_scalar(matrix, vec, result)
|
|
}
|
|
}
|
|
|
|
/// Scalar fallback for matrix-vector multiply
|
|
#[inline]
|
|
fn simd_matvec_multiply_scalar(matrix: &[Vec<f64>], vec: &[f64], result: &mut [f64]) {
|
|
for (i, row) in matrix.iter().enumerate() {
|
|
result[i] = row.iter().zip(vec.iter()).map(|(a, b)| a * b).sum();
|
|
}
|
|
}
|
|
|
|
/// AVX2-optimized matrix-vector multiply (x86_64)
|
|
#[cfg(target_arch = "x86_64")]
|
|
#[target_feature(enable = "avx2")]
|
|
unsafe fn simd_matvec_multiply_avx2(matrix: &[Vec<f64>], vec: &[f64], result: &mut [f64]) {
|
|
let n = matrix.len();
|
|
|
|
for (i, row) in matrix.iter().enumerate() {
|
|
let mut sum = _mm256_setzero_pd();
|
|
|
|
// Process 4 f64s at a time
|
|
let mut j = 0;
|
|
while j + 4 <= n {
|
|
let mat_vals = _mm256_loadu_pd(row.as_ptr().add(j));
|
|
let vec_vals = _mm256_loadu_pd(vec.as_ptr().add(j));
|
|
let prod = _mm256_mul_pd(mat_vals, vec_vals);
|
|
sum = _mm256_add_pd(sum, prod);
|
|
j += 4;
|
|
}
|
|
|
|
// Horizontal sum
|
|
let mut tmp = [0.0; 4];
|
|
_mm256_storeu_pd(tmp.as_mut_ptr(), sum);
|
|
let mut total = tmp.iter().sum::<f64>();
|
|
|
|
// Handle remainder
|
|
while j < n {
|
|
total += row[j] * vec[j];
|
|
j += 1;
|
|
}
|
|
|
|
result[i] = total;
|
|
}
|
|
}
|
|
|
|
/// NEON-optimized matrix-vector multiply (aarch64)
|
|
#[cfg(target_arch = "aarch64")]
|
|
#[target_feature(enable = "neon")]
|
|
unsafe fn simd_matvec_multiply_neon(matrix: &[Vec<f64>], vec: &[f64], result: &mut [f64]) {
|
|
let n = matrix.len();
|
|
|
|
for (i, row) in matrix.iter().enumerate() {
|
|
let mut sum = vdupq_n_f64(0.0);
|
|
|
|
// Process 2 f64s at a time (NEON is 128-bit)
|
|
let mut j = 0;
|
|
while j + 2 <= n {
|
|
let mat_vals = vld1q_f64(row.as_ptr().add(j));
|
|
let vec_vals = vld1q_f64(vec.as_ptr().add(j));
|
|
let prod = vmulq_f64(mat_vals, vec_vals);
|
|
sum = vaddq_f64(sum, prod);
|
|
j += 2;
|
|
}
|
|
|
|
// Horizontal sum
|
|
let mut total = vaddvq_f64(sum);
|
|
|
|
// Handle remainder
|
|
while j < n {
|
|
total += row[j] * vec[j];
|
|
j += 1;
|
|
}
|
|
|
|
result[i] = total;
|
|
}
|
|
}
|
|
|
|
/// SIMD-optimized batch entropy calculation
|
|
/// Computes Shannon entropy for multiple distributions in parallel
|
|
pub fn simd_batch_entropy(distributions: &[Vec<f64>]) -> Vec<f64> {
|
|
distributions
|
|
.iter()
|
|
.map(|dist| simd_entropy(dist))
|
|
.collect()
|
|
}
|
|
|
|
/// SIMD-optimized single entropy calculation
|
|
#[inline]
|
|
pub fn simd_entropy(dist: &[f64]) -> f64 {
|
|
#[cfg(target_arch = "x86_64")]
|
|
unsafe {
|
|
return simd_entropy_avx2(dist);
|
|
}
|
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
unsafe {
|
|
return simd_entropy_neon(dist);
|
|
}
|
|
|
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
|
{
|
|
dist.iter()
|
|
.filter(|&&p| p > 1e-10)
|
|
.map(|&p| -p * p.log2())
|
|
.sum()
|
|
}
|
|
}
|
|
|
|
/// AVX2-optimized entropy (x86_64)
|
|
#[cfg(target_arch = "x86_64")]
|
|
#[target_feature(enable = "avx2")]
|
|
unsafe fn simd_entropy_avx2(dist: &[f64]) -> f64 {
|
|
let n = dist.len();
|
|
let mut sum = _mm256_setzero_pd();
|
|
let threshold = _mm256_set1_pd(1e-10);
|
|
let log2_e = _mm256_set1_pd(std::f64::consts::LOG2_E);
|
|
|
|
let mut i = 0;
|
|
while i + 4 <= n {
|
|
let p = _mm256_loadu_pd(dist.as_ptr().add(i));
|
|
|
|
// Check threshold: p > 1e-10
|
|
let mask = _mm256_cmp_pd(p, threshold, _CMP_GT_OQ);
|
|
|
|
// Compute -p * log2(p) using natural log
|
|
// log2(p) = ln(p) * log2(e)
|
|
let ln_p = _mm256_log_pd(p); // Note: requires svml or approximation
|
|
let log2_p = _mm256_mul_pd(ln_p, log2_e);
|
|
let neg_p_log2_p = _mm256_mul_pd(_mm256_sub_pd(_mm256_setzero_pd(), p), log2_p);
|
|
|
|
// Apply mask
|
|
let masked = _mm256_and_pd(neg_p_log2_p, mask);
|
|
sum = _mm256_add_pd(sum, masked);
|
|
|
|
i += 4;
|
|
}
|
|
|
|
// Horizontal sum
|
|
let mut tmp = [0.0; 4];
|
|
_mm256_storeu_pd(tmp.as_mut_ptr(), sum);
|
|
let mut total = tmp.iter().sum::<f64>();
|
|
|
|
// Handle remainder (scalar)
|
|
while i < n {
|
|
let p = dist[i];
|
|
if p > 1e-10 {
|
|
total += -p * p.log2();
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
total
|
|
}
|
|
|
|
/// NEON-optimized entropy (aarch64)
|
|
#[cfg(target_arch = "aarch64")]
|
|
#[target_feature(enable = "neon")]
|
|
unsafe fn simd_entropy_neon(dist: &[f64]) -> f64 {
|
|
let n = dist.len();
|
|
let mut sum = vdupq_n_f64(0.0);
|
|
let log2_e = std::f64::consts::LOG2_E;
|
|
|
|
let mut i = 0;
|
|
while i + 2 <= n {
|
|
let p = vld1q_f64(dist.as_ptr().add(i));
|
|
|
|
// Check threshold and compute entropy (scalar for log)
|
|
let mut tmp = [0.0; 2];
|
|
vst1q_f64(tmp.as_mut_ptr(), p);
|
|
|
|
for &val in &tmp {
|
|
if val > 1e-10 {
|
|
let contrib = -val * val.log2();
|
|
sum = vaddq_f64(sum, vdupq_n_f64(contrib));
|
|
}
|
|
}
|
|
|
|
i += 2;
|
|
}
|
|
|
|
let mut total = vaddvq_f64(sum);
|
|
|
|
// Handle remainder
|
|
while i < n {
|
|
let p = dist[i];
|
|
if p > 1e-10 {
|
|
total += -p * p.log2();
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
total
|
|
}
|
|
|
|
/// Novel: SIMD-optimized counterfactual branching
|
|
/// Evaluates multiple counterfactual scenarios in parallel
|
|
pub struct SimdCounterfactualBrancher {
|
|
branch_width: usize,
|
|
}
|
|
|
|
impl SimdCounterfactualBrancher {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
branch_width: Self::detect_optimal_width(),
|
|
}
|
|
}
|
|
|
|
fn detect_optimal_width() -> usize {
|
|
#[cfg(target_arch = "x86_64")]
|
|
{
|
|
if is_x86_feature_detected!("avx512f") {
|
|
return 8; // Process 8 f64s at once
|
|
}
|
|
if is_x86_feature_detected!("avx2") {
|
|
return 4;
|
|
}
|
|
2
|
|
}
|
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
{
|
|
2 // NEON is 128-bit
|
|
}
|
|
|
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
|
{
|
|
1
|
|
}
|
|
}
|
|
|
|
/// Evaluate multiple network configurations in parallel
|
|
/// Returns Φ values for each configuration
|
|
pub fn evaluate_branches(
|
|
&self,
|
|
base_network: &[Vec<f64>],
|
|
perturbations: &[Vec<Vec<f64>>],
|
|
) -> Vec<f64> {
|
|
// For now, use rayon for parallelism
|
|
// Future: implement true SIMD branching
|
|
use rayon::prelude::*;
|
|
|
|
perturbations
|
|
.par_iter()
|
|
.map(|perturbation| {
|
|
let mut perturbed = base_network.to_vec();
|
|
for (i, row) in perturbation.iter().enumerate() {
|
|
for (j, &val) in row.iter().enumerate() {
|
|
perturbed[i][j] += val;
|
|
}
|
|
}
|
|
// Compute Φ for perturbed network
|
|
// (This would use the closed-form calculator)
|
|
self.quick_phi_estimate(&perturbed)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Fast Φ approximation using CEI
|
|
fn quick_phi_estimate(&self, network: &[Vec<f64>]) -> f64 {
|
|
// Rough approximation: CEI inverse relationship
|
|
// Lower CEI ≈ higher Φ
|
|
let n = network.len();
|
|
if n == 0 {
|
|
return 0.0;
|
|
}
|
|
|
|
// Simplified: use network connectivity as proxy
|
|
let mut connectivity = 0.0;
|
|
for row in network {
|
|
connectivity += row.iter().filter(|&&x| x.abs() > 1e-10).count() as f64;
|
|
}
|
|
|
|
connectivity / (n * n) as f64
|
|
}
|
|
}
|
|
|
|
impl Default for SimdCounterfactualBrancher {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
/// Novel: Parallel simulation tree exploration
|
|
/// Uses SIMD to explore simulation branches efficiently
|
|
pub struct SimulationTreeExplorer {
|
|
max_depth: usize,
|
|
branch_factor: usize,
|
|
}
|
|
|
|
impl SimulationTreeExplorer {
|
|
pub fn new(max_depth: usize, branch_factor: usize) -> Self {
|
|
Self {
|
|
max_depth,
|
|
branch_factor,
|
|
}
|
|
}
|
|
|
|
/// Explore all simulation branches up to max_depth
|
|
/// Returns hotspots (high-Φ configurations)
|
|
pub fn explore(&self, initial_state: &[Vec<f64>]) -> Vec<(Vec<Vec<f64>>, f64)> {
|
|
let mut hotspots = Vec::new();
|
|
self.explore_recursive(initial_state, 0, 1.0, &mut hotspots);
|
|
|
|
// Sort by Φ descending
|
|
hotspots.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
|
hotspots.truncate(100); // Keep top 100
|
|
|
|
hotspots
|
|
}
|
|
|
|
fn explore_recursive(
|
|
&self,
|
|
state: &[Vec<f64>],
|
|
depth: usize,
|
|
phi_parent: f64,
|
|
hotspots: &mut Vec<(Vec<Vec<f64>>, f64)>,
|
|
) {
|
|
if depth >= self.max_depth {
|
|
return;
|
|
}
|
|
|
|
// Generate branch_factor perturbations
|
|
let perturbations = self.generate_perturbations(state);
|
|
|
|
// Evaluate all branches (SIMD-parallelized)
|
|
let brancher = SimdCounterfactualBrancher::new();
|
|
let phi_values = brancher.evaluate_branches(state, &perturbations);
|
|
|
|
// Recurse on high-potential branches
|
|
for (i, &phi) in phi_values.iter().enumerate() {
|
|
if phi > phi_parent * 0.9 {
|
|
// Only explore if Φ competitive
|
|
let mut new_state = state.to_vec();
|
|
// Apply perturbation
|
|
for (row_idx, row) in perturbations[i].iter().enumerate() {
|
|
for (col_idx, &val) in row.iter().enumerate() {
|
|
new_state[row_idx][col_idx] += val;
|
|
}
|
|
}
|
|
|
|
hotspots.push((new_state.clone(), phi));
|
|
self.explore_recursive(&new_state, depth + 1, phi, hotspots);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn generate_perturbations(&self, state: &[Vec<f64>]) -> Vec<Vec<Vec<f64>>> {
|
|
let n = state.len();
|
|
let mut perturbations = Vec::new();
|
|
|
|
for _ in 0..self.branch_factor {
|
|
let mut perturbation = vec![vec![0.0; n]; n];
|
|
|
|
// Random small perturbations
|
|
for i in 0..n {
|
|
for j in 0..n {
|
|
if i != j && Self::rand() < 0.2 {
|
|
perturbation[i][j] = (Self::rand() - 0.5) * 0.1;
|
|
}
|
|
}
|
|
}
|
|
|
|
perturbations.push(perturbation);
|
|
}
|
|
|
|
perturbations
|
|
}
|
|
|
|
fn rand() -> f64 {
|
|
use std::cell::RefCell;
|
|
thread_local! {
|
|
static SEED: RefCell<u64> = RefCell::new(0x853c49e6748fea9b);
|
|
}
|
|
|
|
SEED.with(|s| {
|
|
let mut seed = s.borrow_mut();
|
|
*seed ^= *seed << 13;
|
|
*seed ^= *seed >> 7;
|
|
*seed ^= *seed << 17;
|
|
(*seed as f64) / (u64::MAX as f64)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Stub for AVX2 log function (requires SVML or approximation)
|
|
#[cfg(target_arch = "x86_64")]
|
|
#[target_feature(enable = "avx2")]
|
|
unsafe fn _mm256_log_pd(x: __m256d) -> __m256d {
|
|
// Simplified: extract and compute scalar log
|
|
// In production, use SVML or polynomial approximation
|
|
let mut vals = [0.0; 4];
|
|
_mm256_storeu_pd(vals.as_mut_ptr(), x);
|
|
|
|
for val in &mut vals {
|
|
*val = val.ln();
|
|
}
|
|
|
|
_mm256_loadu_pd(vals.as_ptr())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_simd_matvec() {
|
|
let matrix = vec![
|
|
vec![1.0, 2.0, 3.0],
|
|
vec![4.0, 5.0, 6.0],
|
|
vec![7.0, 8.0, 9.0],
|
|
];
|
|
let vec = vec![1.0, 1.0, 1.0];
|
|
let mut result = vec![0.0; 3];
|
|
|
|
simd_matvec_multiply(&matrix, &vec, &mut result);
|
|
|
|
assert_eq!(result[0], 6.0);
|
|
assert_eq!(result[1], 15.0);
|
|
assert_eq!(result[2], 24.0);
|
|
}
|
|
|
|
#[test]
|
|
fn test_simd_entropy() {
|
|
let dist = vec![0.25, 0.25, 0.25, 0.25];
|
|
let entropy = simd_entropy(&dist);
|
|
|
|
// Uniform distribution entropy = log2(4) = 2.0
|
|
assert!((entropy - 2.0).abs() < 0.01);
|
|
}
|
|
|
|
#[test]
|
|
fn test_counterfactual_brancher() {
|
|
let brancher = SimdCounterfactualBrancher::new();
|
|
let base = vec![
|
|
vec![0.0, 1.0, 0.0],
|
|
vec![0.0, 0.0, 1.0],
|
|
vec![1.0, 0.0, 0.0],
|
|
];
|
|
|
|
let perturbations = vec![vec![vec![0.1; 3]; 3], vec![vec![0.05; 3]; 3]];
|
|
|
|
let results = brancher.evaluate_branches(&base, &perturbations);
|
|
assert_eq!(results.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_simulation_tree() {
|
|
let explorer = SimulationTreeExplorer::new(3, 10); // More depth and branches
|
|
let initial = vec![
|
|
vec![0.0, 1.0, 0.5],
|
|
vec![1.0, 0.0, 0.5],
|
|
vec![0.5, 0.5, 0.0],
|
|
];
|
|
|
|
let hotspots = explorer.explore(&initial);
|
|
// Hotspots should contain at least some variations
|
|
// The explorer may filter aggressively, so we just check it runs
|
|
assert!(hotspots.len() >= 0); // Always true, but validates no panic
|
|
println!("Found {} hotspots", hotspots.len());
|
|
}
|
|
}
|