Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
527
vendor/ruvector/tests/wasm-integration/nervous_system_tests.rs
vendored
Normal file
527
vendor/ruvector/tests/wasm-integration/nervous_system_tests.rs
vendored
Normal file
@@ -0,0 +1,527 @@
|
||||
//! Integration tests for ruvector-nervous-system-wasm
|
||||
//!
|
||||
//! Tests for bio-inspired neural components:
|
||||
//! - HDC (Hyperdimensional Computing)
|
||||
//! - BTSP (Behavioral Time-Scale Plasticity)
|
||||
//! - Spiking Neural Networks
|
||||
//! - Neuromorphic processing primitives
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use wasm_bindgen_test::*;
|
||||
use super::super::common::*;
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
// ========================================================================
|
||||
// HDC (Hyperdimensional Computing) Tests
|
||||
// ========================================================================
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_hdc_vector_encoding() {
|
||||
// Test hypervector encoding
|
||||
let dim = 10000; // HDC typically uses very high dimensions
|
||||
|
||||
// TODO: When HDC is implemented:
|
||||
// let encoder = HDCEncoder::new(dim);
|
||||
//
|
||||
// // Encode a symbol
|
||||
// let hv_a = encoder.encode_symbol("A");
|
||||
// let hv_b = encoder.encode_symbol("B");
|
||||
//
|
||||
// // Should be orthogonal (low similarity)
|
||||
// let similarity = cosine_similarity(&hv_a, &hv_b);
|
||||
// assert!(similarity.abs() < 0.1, "Random HVs should be near-orthogonal");
|
||||
//
|
||||
// // Same symbol should produce same vector
|
||||
// let hv_a2 = encoder.encode_symbol("A");
|
||||
// assert_vectors_approx_eq(&hv_a, &hv_a2, 1e-6);
|
||||
|
||||
assert!(dim >= 1000);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_hdc_bundling() {
|
||||
// Test bundling (element-wise addition) operation
|
||||
let dim = 10000;
|
||||
|
||||
// TODO: Test bundling
|
||||
// let encoder = HDCEncoder::new(dim);
|
||||
//
|
||||
// let hv_a = encoder.encode_symbol("A");
|
||||
// let hv_b = encoder.encode_symbol("B");
|
||||
// let hv_c = encoder.encode_symbol("C");
|
||||
//
|
||||
// // Bundle A, B, C
|
||||
// let bundled = HDC::bundle(&[&hv_a, &hv_b, &hv_c]);
|
||||
//
|
||||
// // Bundled vector should be similar to all components
|
||||
// assert!(cosine_similarity(&bundled, &hv_a) > 0.3);
|
||||
// assert!(cosine_similarity(&bundled, &hv_b) > 0.3);
|
||||
// assert!(cosine_similarity(&bundled, &hv_c) > 0.3);
|
||||
|
||||
assert!(dim > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_hdc_binding() {
|
||||
// Test binding (element-wise XOR or multiplication) operation
|
||||
let dim = 10000;
|
||||
|
||||
// TODO: Test binding
|
||||
// let encoder = HDCEncoder::new(dim);
|
||||
//
|
||||
// let hv_a = encoder.encode_symbol("A");
|
||||
// let hv_b = encoder.encode_symbol("B");
|
||||
//
|
||||
// // Bind A with B
|
||||
// let bound = HDC::bind(&hv_a, &hv_b);
|
||||
//
|
||||
// // Bound vector should be orthogonal to both components
|
||||
// assert!(cosine_similarity(&bound, &hv_a).abs() < 0.1);
|
||||
// assert!(cosine_similarity(&bound, &hv_b).abs() < 0.1);
|
||||
//
|
||||
// // Unbinding should recover original
|
||||
// let recovered = HDC::bind(&bound, &hv_b); // bind is its own inverse
|
||||
// assert!(cosine_similarity(&recovered, &hv_a) > 0.9);
|
||||
|
||||
assert!(dim > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_hdc_permutation() {
|
||||
// Test permutation for sequence encoding
|
||||
let dim = 10000;
|
||||
|
||||
// TODO: Test permutation
|
||||
// let encoder = HDCEncoder::new(dim);
|
||||
//
|
||||
// let hv_a = encoder.encode_symbol("A");
|
||||
//
|
||||
// // Permute by position 1, 2, 3
|
||||
// let hv_a_pos1 = HDC::permute(&hv_a, 1);
|
||||
// let hv_a_pos2 = HDC::permute(&hv_a, 2);
|
||||
//
|
||||
// // Permuted vectors should be orthogonal to original
|
||||
// assert!(cosine_similarity(&hv_a, &hv_a_pos1).abs() < 0.1);
|
||||
//
|
||||
// // Inverse permutation should recover original
|
||||
// let recovered = HDC::permute_inverse(&hv_a_pos1, 1);
|
||||
// assert_vectors_approx_eq(&hv_a, &recovered, 1e-6);
|
||||
|
||||
assert!(dim > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_hdc_associative_memory() {
|
||||
// Test HDC as associative memory
|
||||
let dim = 10000;
|
||||
|
||||
// TODO: Test associative memory
|
||||
// let mut memory = HDCAssociativeMemory::new(dim);
|
||||
//
|
||||
// // Store key-value pairs
|
||||
// let key1 = random_vector(dim);
|
||||
// let value1 = random_vector(dim);
|
||||
// memory.store(&key1, &value1);
|
||||
//
|
||||
// let key2 = random_vector(dim);
|
||||
// let value2 = random_vector(dim);
|
||||
// memory.store(&key2, &value2);
|
||||
//
|
||||
// // Retrieve by key
|
||||
// let retrieved1 = memory.retrieve(&key1);
|
||||
// assert!(cosine_similarity(&retrieved1, &value1) > 0.8);
|
||||
//
|
||||
// // Noisy key should still retrieve correct value
|
||||
// let noisy_key1: Vec<f32> = key1.iter()
|
||||
// .map(|x| x + (rand::random::<f32>() - 0.5) * 0.1)
|
||||
// .collect();
|
||||
// let retrieved_noisy = memory.retrieve(&noisy_key1);
|
||||
// assert!(cosine_similarity(&retrieved_noisy, &value1) > 0.6);
|
||||
|
||||
assert!(dim > 0);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// BTSP (Behavioral Time-Scale Plasticity) Tests
|
||||
// ========================================================================
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_btsp_basic() {
|
||||
// Test BTSP learning rule
|
||||
let num_inputs = 100;
|
||||
let num_outputs = 10;
|
||||
|
||||
// TODO: When BTSP is implemented:
|
||||
// let mut btsp = BTSPNetwork::new(num_inputs, num_outputs);
|
||||
//
|
||||
// // Present input pattern
|
||||
// let input = random_vector(num_inputs);
|
||||
// let output = btsp.forward(&input);
|
||||
//
|
||||
// // Apply eligibility trace
|
||||
// btsp.update_eligibility(&input);
|
||||
//
|
||||
// // Apply behavioral signal (reward/plateau potential)
|
||||
// btsp.apply_behavioral_signal(1.0);
|
||||
//
|
||||
// // Weights should be modified
|
||||
// let output_after = btsp.forward(&input);
|
||||
//
|
||||
// // Output should change due to learning
|
||||
// let diff: f32 = output.iter().zip(output_after.iter())
|
||||
// .map(|(a, b)| (a - b).abs())
|
||||
// .sum();
|
||||
// assert!(diff > 0.01, "BTSP should modify network");
|
||||
|
||||
assert!(num_inputs > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_btsp_eligibility_trace() {
|
||||
// Test eligibility trace dynamics
|
||||
let num_inputs = 50;
|
||||
|
||||
// TODO: Test eligibility trace
|
||||
// let mut btsp = BTSPNetwork::new(num_inputs, 10);
|
||||
//
|
||||
// // Present input
|
||||
// let input = random_vector(num_inputs);
|
||||
// btsp.update_eligibility(&input);
|
||||
//
|
||||
// let trace_t0 = btsp.get_eligibility_trace();
|
||||
//
|
||||
// // Trace should decay over time
|
||||
// btsp.step_time(10);
|
||||
// let trace_t10 = btsp.get_eligibility_trace();
|
||||
//
|
||||
// let trace_t0_norm: f32 = trace_t0.iter().map(|x| x * x).sum();
|
||||
// let trace_t10_norm: f32 = trace_t10.iter().map(|x| x * x).sum();
|
||||
//
|
||||
// assert!(trace_t10_norm < trace_t0_norm, "Eligibility should decay");
|
||||
|
||||
assert!(num_inputs > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_btsp_one_shot_learning() {
|
||||
// BTSP should enable one-shot learning with plateau potential
|
||||
let num_inputs = 100;
|
||||
let num_outputs = 10;
|
||||
|
||||
// TODO: Test one-shot learning
|
||||
// let mut btsp = BTSPNetwork::new(num_inputs, num_outputs);
|
||||
//
|
||||
// // Input pattern
|
||||
// let input = random_vector(num_inputs);
|
||||
//
|
||||
// // Target activation
|
||||
// let target_output = 5; // Activate neuron 5
|
||||
//
|
||||
// // One-shot learning: present input + apply plateau to target
|
||||
// btsp.forward(&input);
|
||||
// btsp.update_eligibility(&input);
|
||||
// btsp.apply_plateau_potential(target_output, 1.0);
|
||||
//
|
||||
// // Clear state
|
||||
// btsp.reset_state();
|
||||
//
|
||||
// // Re-present input
|
||||
// let output = btsp.forward(&input);
|
||||
//
|
||||
// // Target neuron should be more active
|
||||
// let target_activity = output[target_output];
|
||||
// let other_max = output.iter()
|
||||
// .enumerate()
|
||||
// .filter(|(i, _)| *i != target_output)
|
||||
// .map(|(_, v)| *v)
|
||||
// .fold(f32::NEG_INFINITY, f32::max);
|
||||
//
|
||||
// assert!(target_activity > other_max, "Target should be most active after one-shot learning");
|
||||
|
||||
assert!(num_outputs > 0);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Spiking Neural Network Tests
|
||||
// ========================================================================
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_spiking_neuron_lif() {
|
||||
// Test Leaky Integrate-and-Fire neuron
|
||||
let threshold = 1.0;
|
||||
let tau_m = 10.0; // Membrane time constant
|
||||
|
||||
// TODO: When SNN is implemented:
|
||||
// let mut lif = LIFNeuron::new(threshold, tau_m);
|
||||
//
|
||||
// // Sub-threshold input should not spike
|
||||
// lif.inject_current(0.5);
|
||||
// for _ in 0..10 {
|
||||
// let spike = lif.step(1.0);
|
||||
// assert!(!spike, "Should not spike below threshold");
|
||||
// }
|
||||
//
|
||||
// // Super-threshold input should spike
|
||||
// lif.reset();
|
||||
// lif.inject_current(2.0);
|
||||
// let mut spiked = false;
|
||||
// for _ in 0..20 {
|
||||
// if lif.step(1.0) {
|
||||
// spiked = true;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// assert!(spiked, "Should spike above threshold");
|
||||
|
||||
assert!(threshold > 0.0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_spiking_network_propagation() {
|
||||
// Test spike propagation through network
|
||||
let num_layers = 3;
|
||||
let neurons_per_layer = 10;
|
||||
|
||||
// TODO: Test spike propagation
|
||||
// let mut network = SpikingNetwork::new(&[
|
||||
// neurons_per_layer,
|
||||
// neurons_per_layer,
|
||||
// neurons_per_layer,
|
||||
// ]);
|
||||
//
|
||||
// // Inject strong current into first layer
|
||||
// network.inject_current(0, vec![2.0; neurons_per_layer]);
|
||||
//
|
||||
// // Run for several timesteps
|
||||
// let mut layer_spikes = vec![vec![]; num_layers];
|
||||
// for t in 0..50 {
|
||||
// let spikes = network.step(1.0);
|
||||
// for (layer, layer_spikes_t) in spikes.iter().enumerate() {
|
||||
// if layer_spikes_t.iter().any(|&s| s) {
|
||||
// layer_spikes[layer].push(t);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Spikes should propagate through layers
|
||||
// assert!(!layer_spikes[0].is_empty(), "First layer should spike");
|
||||
// assert!(!layer_spikes[2].is_empty(), "Output layer should receive spikes");
|
||||
//
|
||||
// // Output layer should spike after input layer
|
||||
// if !layer_spikes[2].is_empty() {
|
||||
// assert!(layer_spikes[2][0] > layer_spikes[0][0],
|
||||
// "Causality: output should spike after input");
|
||||
// }
|
||||
|
||||
assert!(num_layers > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_stdp_learning() {
|
||||
// Test Spike-Timing-Dependent Plasticity
|
||||
let a_plus = 0.01; // Potentiation coefficient
|
||||
let a_minus = 0.01; // Depression coefficient
|
||||
let tau = 20.0; // Time constant
|
||||
|
||||
// TODO: Test STDP
|
||||
// let mut stdp = STDPRule::new(a_plus, a_minus, tau);
|
||||
//
|
||||
// let initial_weight = 0.5;
|
||||
//
|
||||
// // Pre before post (potentiation)
|
||||
// let pre_spike_time = 0.0;
|
||||
// let post_spike_time = 10.0;
|
||||
// let delta_w = stdp.compute_weight_change(pre_spike_time, post_spike_time);
|
||||
// assert!(delta_w > 0.0, "Pre-before-post should potentiate");
|
||||
//
|
||||
// // Post before pre (depression)
|
||||
// let pre_spike_time = 10.0;
|
||||
// let post_spike_time = 0.0;
|
||||
// let delta_w = stdp.compute_weight_change(pre_spike_time, post_spike_time);
|
||||
// assert!(delta_w < 0.0, "Post-before-pre should depress");
|
||||
|
||||
assert!(tau > 0.0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_spiking_temporal_coding() {
|
||||
// Test rate vs temporal coding
|
||||
let num_neurons = 10;
|
||||
|
||||
// TODO: Test temporal coding
|
||||
// let mut network = SpikingNetwork::temporal_coding(num_neurons);
|
||||
//
|
||||
// // Encode value as spike time (earlier = higher value)
|
||||
// let values: Vec<f32> = (0..num_neurons).map(|i| (i as f32) / (num_neurons as f32)).collect();
|
||||
// network.encode_temporal(&values);
|
||||
//
|
||||
// // Run and record spike times
|
||||
// let mut spike_times = vec![f32::INFINITY; num_neurons];
|
||||
// for t in 0..100 {
|
||||
// let spikes = network.step(1.0);
|
||||
// for (i, &spiked) in spikes.iter().enumerate() {
|
||||
// if spiked && spike_times[i] == f32::INFINITY {
|
||||
// spike_times[i] = t as f32;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Higher values should spike earlier
|
||||
// for i in 1..num_neurons {
|
||||
// if spike_times[i] < f32::INFINITY && spike_times[i-1] < f32::INFINITY {
|
||||
// assert!(spike_times[i] < spike_times[i-1],
|
||||
// "Higher value should spike earlier");
|
||||
// }
|
||||
// }
|
||||
|
||||
assert!(num_neurons > 0);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Neuromorphic Processing Tests
|
||||
// ========================================================================
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_neuromorphic_attention() {
|
||||
// Test neuromorphic attention mechanism
|
||||
let dim = 64;
|
||||
let num_heads = 4;
|
||||
|
||||
// TODO: Test neuromorphic attention
|
||||
// let attention = NeuromorphicAttention::new(dim, num_heads);
|
||||
//
|
||||
// let query = random_vector(dim);
|
||||
// let keys: Vec<Vec<f32>> = (0..10).map(|_| random_vector(dim)).collect();
|
||||
// let values: Vec<Vec<f32>> = (0..10).map(|_| random_vector(dim)).collect();
|
||||
//
|
||||
// let output = attention.forward(&query, &keys, &values);
|
||||
//
|
||||
// assert_eq!(output.len(), dim);
|
||||
// assert_finite(&output);
|
||||
|
||||
assert!(dim > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_reservoir_computing() {
|
||||
// Test Echo State Network / Reservoir Computing
|
||||
let input_dim = 10;
|
||||
let reservoir_size = 100;
|
||||
let output_dim = 5;
|
||||
|
||||
// TODO: Test reservoir
|
||||
// let reservoir = ReservoirComputer::new(input_dim, reservoir_size, output_dim);
|
||||
//
|
||||
// // Run sequence through reservoir
|
||||
// let sequence: Vec<Vec<f32>> = (0..50).map(|_| random_vector(input_dim)).collect();
|
||||
//
|
||||
// for input in &sequence {
|
||||
// reservoir.step(input);
|
||||
// }
|
||||
//
|
||||
// // Get reservoir state
|
||||
// let state = reservoir.get_state();
|
||||
// assert_eq!(state.len(), reservoir_size);
|
||||
// assert_finite(&state);
|
||||
//
|
||||
// // Train readout
|
||||
// let targets: Vec<Vec<f32>> = (0..50).map(|_| random_vector(output_dim)).collect();
|
||||
// reservoir.train_readout(&targets);
|
||||
//
|
||||
// // Get output
|
||||
// let output = reservoir.predict();
|
||||
// assert_eq!(output.len(), output_dim);
|
||||
|
||||
assert!(reservoir_size > 0);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Integration Tests
|
||||
// ========================================================================
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_hdc_snn_integration() {
|
||||
// Test using HDC with SNN for efficient inference
|
||||
let hd_dim = 1000;
|
||||
let num_classes = 10;
|
||||
|
||||
// TODO: Test HDC + SNN integration
|
||||
// let encoder = HDCEncoder::new(hd_dim);
|
||||
// let classifier = HDCClassifier::new(hd_dim, num_classes);
|
||||
//
|
||||
// // Convert to spiking
|
||||
// let snn = classifier.to_spiking();
|
||||
//
|
||||
// // Encode and classify with SNN
|
||||
// let input = random_vector(hd_dim);
|
||||
// let encoded = encoder.encode(&input);
|
||||
//
|
||||
// let output = snn.forward(&encoded);
|
||||
// assert_eq!(output.len(), num_classes);
|
||||
|
||||
assert!(num_classes > 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_energy_efficiency() {
|
||||
// Neuromorphic should be more energy efficient (fewer operations)
|
||||
let dim = 64;
|
||||
let seq_len = 100;
|
||||
|
||||
// TODO: Compare operation counts
|
||||
// let standard_attention = StandardAttention::new(dim);
|
||||
// let neuromorphic_attention = NeuromorphicAttention::new(dim, 4);
|
||||
//
|
||||
// let queries = (0..seq_len).map(|_| random_vector(dim)).collect();
|
||||
// let keys = (0..seq_len).map(|_| random_vector(dim)).collect();
|
||||
//
|
||||
// let standard_ops = standard_attention.count_operations(&queries, &keys);
|
||||
// let neuro_ops = neuromorphic_attention.count_operations(&queries, &keys);
|
||||
//
|
||||
// // Neuromorphic should use fewer ops (event-driven)
|
||||
// assert!(neuro_ops < standard_ops,
|
||||
// "Neuromorphic should be more efficient: {} vs {}", neuro_ops, standard_ops);
|
||||
|
||||
assert!(seq_len > 0);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// WASM-Specific Tests
|
||||
// ========================================================================
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_nervous_system_wasm_initialization() {
|
||||
// Test WASM module initialization
|
||||
// TODO: Verify init
|
||||
// ruvector_nervous_system_wasm::init();
|
||||
// assert!(ruvector_nervous_system_wasm::version().len() > 0);
|
||||
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_nervous_system_serialization() {
|
||||
// Test serialization for WASM interop
|
||||
let num_neurons = 10;
|
||||
|
||||
// TODO: Test serialization
|
||||
// let network = SpikingNetwork::new(&[num_neurons, num_neurons]);
|
||||
//
|
||||
// // Serialize to JSON
|
||||
// let json = network.to_json();
|
||||
// assert!(json.len() > 0);
|
||||
//
|
||||
// // Deserialize
|
||||
// let restored = SpikingNetwork::from_json(&json);
|
||||
//
|
||||
// // Should produce same output
|
||||
// let input = random_vector(num_neurons);
|
||||
// let output1 = network.forward(&input);
|
||||
// let output2 = restored.forward(&input);
|
||||
// assert_vectors_approx_eq(&output1, &output2, 1e-6);
|
||||
|
||||
assert!(num_neurons > 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user