//! 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 = key1.iter() // .map(|x| x + (rand::random::() - 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 = (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> = (0..10).map(|_| random_vector(dim)).collect(); // let values: Vec> = (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> = (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> = (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); } }