Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,212 @@
//! Configuration types for the neural coherence gate.
use serde::{Deserialize, Serialize};
/// Configuration for the neural coherence gate.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeuralGateConfig {
/// Hysteresis configuration.
pub hysteresis: HysteresisConfig,
/// Global workspace configuration.
pub workspace: WorkspaceConfig,
/// Oscillator configuration for routing.
pub oscillator: OscillatorConfig,
/// HDC hypervector dimension.
pub hdc_dimension: usize,
/// Memory capacity for witness storage.
pub memory_capacity: usize,
/// Dendritic coincidence window in microseconds.
pub coincidence_window_us: u64,
/// Number of dendritic branches.
pub num_branches: usize,
/// Enable oscillatory routing.
pub enable_oscillatory_routing: bool,
}
impl Default for NeuralGateConfig {
fn default() -> Self {
Self {
hysteresis: HysteresisConfig::default(),
workspace: WorkspaceConfig::default(),
oscillator: OscillatorConfig::default(),
hdc_dimension: 10000, // 10K-dimensional hypervectors
memory_capacity: 10000,
coincidence_window_us: 5000, // 5ms window
num_branches: 8,
enable_oscillatory_routing: true,
}
}
}
/// Configuration for hysteresis tracking.
///
/// Hysteresis prevents rapid oscillation between decision states
/// by requiring a threshold to be crossed by a margin before switching.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct HysteresisConfig {
/// Lower threshold for switching to "low" state.
pub low_threshold: f32,
/// Upper threshold for switching to "high" state.
pub high_threshold: f32,
/// Minimum time to stay in a state before switching (ms).
pub min_dwell_time_ms: u64,
/// Smoothing factor for energy (0 = no smoothing, 1 = full smoothing).
pub smoothing_factor: f32,
}
impl Default for HysteresisConfig {
fn default() -> Self {
Self {
low_threshold: 0.3,
high_threshold: 0.7,
min_dwell_time_ms: 100,
smoothing_factor: 0.2,
}
}
}
impl HysteresisConfig {
/// Create a sensitive hysteresis configuration (smaller band).
#[must_use]
pub fn sensitive() -> Self {
Self {
low_threshold: 0.4,
high_threshold: 0.6,
min_dwell_time_ms: 50,
smoothing_factor: 0.1,
}
}
/// Create a stable hysteresis configuration (larger band).
#[must_use]
pub fn stable() -> Self {
Self {
low_threshold: 0.2,
high_threshold: 0.8,
min_dwell_time_ms: 200,
smoothing_factor: 0.3,
}
}
/// Check if the configuration is valid.
#[must_use]
pub fn is_valid(&self) -> bool {
self.low_threshold >= 0.0
&& self.high_threshold <= 1.0
&& self.low_threshold < self.high_threshold
&& self.smoothing_factor >= 0.0
&& self.smoothing_factor <= 1.0
}
}
/// Configuration for the global workspace.
///
/// The global workspace implements the "conscious access" mechanism,
/// broadcasting significant decisions to all modules.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceConfig {
/// Capacity of the workspace buffer.
pub buffer_capacity: usize,
/// Significance threshold for broadcast.
pub broadcast_threshold: f32,
/// Enable attention-based selection.
pub attention_selection: bool,
/// Competition decay factor.
pub competition_decay: f32,
/// Number of competitor slots.
pub num_competitors: usize,
}
impl Default for WorkspaceConfig {
fn default() -> Self {
Self {
buffer_capacity: 100,
broadcast_threshold: 0.6,
attention_selection: true,
competition_decay: 0.9,
num_competitors: 8,
}
}
}
/// Configuration for oscillatory routing.
///
/// Uses the Kuramoto model for phase-based routing of information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OscillatorConfig {
/// Number of oscillators.
pub num_oscillators: usize,
/// Natural frequency (Hz).
pub natural_frequency: f32,
/// Coupling strength.
pub coupling_strength: f32,
/// Phase noise standard deviation.
pub phase_noise: f32,
/// Synchronization threshold for routing.
pub sync_threshold: f32,
}
impl Default for OscillatorConfig {
fn default() -> Self {
Self {
num_oscillators: 64,
natural_frequency: 40.0, // Gamma band (40 Hz)
coupling_strength: 0.5,
phase_noise: 0.1,
sync_threshold: 0.8,
}
}
}
impl OscillatorConfig {
/// Create configuration for fast oscillations (beta band).
#[must_use]
pub fn beta_band() -> Self {
Self {
num_oscillators: 64,
natural_frequency: 20.0, // Beta band
coupling_strength: 0.4,
phase_noise: 0.15,
sync_threshold: 0.75,
}
}
/// Create configuration for slow oscillations (theta band).
#[must_use]
pub fn theta_band() -> Self {
Self {
num_oscillators: 32,
natural_frequency: 6.0, // Theta band
coupling_strength: 0.6,
phase_noise: 0.05,
sync_threshold: 0.85,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hysteresis_validity() {
assert!(HysteresisConfig::default().is_valid());
assert!(HysteresisConfig::sensitive().is_valid());
assert!(HysteresisConfig::stable().is_valid());
let invalid = HysteresisConfig {
low_threshold: 0.8,
high_threshold: 0.3, // Less than low
min_dwell_time_ms: 100,
smoothing_factor: 0.2,
};
assert!(!invalid.is_valid());
}
#[test]
fn test_default_configs() {
let config = NeuralGateConfig::default();
assert_eq!(config.hdc_dimension, 10000);
assert!(config.hysteresis.is_valid());
}
}

View File

@@ -0,0 +1,248 @@
//! Neural decision types.
use serde::{Deserialize, Serialize};
/// Trigger that caused the decision.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DecisionTrigger {
/// Energy crossed a threshold.
EnergyThreshold {
/// The threshold that was crossed.
threshold: f32,
/// Direction of crossing (true = upward).
upward: bool,
},
/// Dendritic coincidence detection fired.
DendriticCoincidence {
/// Number of active synapses.
active_synapses: usize,
/// Required threshold.
threshold: usize,
},
/// Hysteresis state change.
HysteresisChange {
/// Previous state.
from_state: HysteresisState,
/// New state.
to_state: HysteresisState,
},
/// Oscillator synchronization detected.
OscillatorSync {
/// Phase coherence measure.
coherence: f32,
},
/// Workspace broadcast triggered.
WorkspaceBroadcast {
/// Significance score.
significance: f32,
},
/// Manual evaluation request.
Manual,
}
/// Hysteresis state.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum HysteresisState {
/// Low energy state (coherent).
Low,
/// Transition state (uncertain).
Transition,
/// High energy state (incoherent).
High,
}
/// Confidence level of a decision.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct DecisionConfidence {
/// Overall confidence (0.0 to 1.0).
pub overall: f32,
/// Confidence from energy analysis.
pub energy_confidence: f32,
/// Confidence from dendritic processing.
pub dendritic_confidence: f32,
/// Confidence from oscillatory routing.
pub oscillator_confidence: f32,
/// Number of evidence sources supporting the decision.
pub supporting_evidence: usize,
}
impl DecisionConfidence {
/// Create a new decision confidence.
pub fn new(
energy_confidence: f32,
dendritic_confidence: f32,
oscillator_confidence: f32,
supporting_evidence: usize,
) -> Self {
// Combine confidences with weighted average
let overall =
(energy_confidence * 0.4 + dendritic_confidence * 0.3 + oscillator_confidence * 0.3)
.clamp(0.0, 1.0);
Self {
overall,
energy_confidence,
dendritic_confidence,
oscillator_confidence,
supporting_evidence,
}
}
/// Create a low-confidence decision.
pub fn low() -> Self {
Self {
overall: 0.3,
energy_confidence: 0.3,
dendritic_confidence: 0.3,
oscillator_confidence: 0.3,
supporting_evidence: 0,
}
}
/// Create a high-confidence decision.
pub fn high() -> Self {
Self {
overall: 0.95,
energy_confidence: 0.95,
dendritic_confidence: 0.95,
oscillator_confidence: 0.95,
supporting_evidence: 5,
}
}
/// Check if the confidence is high enough to trust.
pub fn is_trustworthy(&self) -> bool {
self.overall >= 0.7 && self.supporting_evidence >= 2
}
}
impl Default for DecisionConfidence {
fn default() -> Self {
Self::low()
}
}
/// Decision from the neural coherence gate.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeuralDecision {
/// Whether to allow the action.
pub allow: bool,
/// Confidence in the decision.
pub confidence: DecisionConfidence,
/// Current hysteresis state.
pub hysteresis_state: HysteresisState,
/// What triggered this decision.
pub trigger: DecisionTrigger,
/// Current energy level.
pub energy: f32,
/// Smoothed energy level.
pub smoothed_energy: f32,
/// Timestamp of the decision.
pub timestamp_ms: u64,
/// Whether this decision should be broadcast.
pub should_broadcast: bool,
}
impl NeuralDecision {
/// Create a new neural decision.
pub fn new(
allow: bool,
energy: f32,
smoothed_energy: f32,
hysteresis_state: HysteresisState,
trigger: DecisionTrigger,
confidence: DecisionConfidence,
) -> Self {
let should_broadcast = !allow || confidence.overall > 0.9;
Self {
allow,
confidence,
hysteresis_state,
trigger,
energy,
smoothed_energy,
timestamp_ms: current_time_ms(),
should_broadcast,
}
}
/// Create an allowing decision.
pub fn allow(energy: f32) -> Self {
Self::new(
true,
energy,
energy,
HysteresisState::Low,
DecisionTrigger::Manual,
DecisionConfidence::high(),
)
}
/// Create a denying decision.
pub fn deny(energy: f32, reason: DecisionTrigger) -> Self {
Self::new(
false,
energy,
energy,
HysteresisState::High,
reason,
DecisionConfidence::high(),
)
}
/// Check if this decision is significant enough to log.
pub fn is_significant(&self) -> bool {
!self.allow || self.confidence.overall > 0.8 || self.should_broadcast
}
/// Get a human-readable description of the decision.
pub fn description(&self) -> String {
let action = if self.allow { "ALLOW" } else { "DENY" };
let state = match self.hysteresis_state {
HysteresisState::Low => "coherent",
HysteresisState::Transition => "uncertain",
HysteresisState::High => "incoherent",
};
format!(
"{} (energy={:.3}, state={}, confidence={:.2})",
action, self.energy, state, self.confidence.overall
)
}
}
/// Get current time in milliseconds.
fn current_time_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decision_confidence() {
let low = DecisionConfidence::low();
assert!(!low.is_trustworthy());
let high = DecisionConfidence::high();
assert!(high.is_trustworthy());
let mixed = DecisionConfidence::new(0.8, 0.7, 0.6, 3);
assert!(mixed.is_trustworthy());
}
#[test]
fn test_neural_decision() {
let allow = NeuralDecision::allow(0.1);
assert!(allow.allow);
assert_eq!(allow.hysteresis_state, HysteresisState::Low);
let deny = NeuralDecision::deny(0.9, DecisionTrigger::Manual);
assert!(!deny.allow);
assert!(deny.is_significant());
}
}

View File

@@ -0,0 +1,379 @@
//! Hyperdimensional computing (HDC) encoding for witnesses.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Default hypervector dimension.
pub const DEFAULT_HDC_DIM: usize = 10000;
/// Operations on hypervectors.
pub trait HypervectorOps {
/// Bind two hypervectors (element-wise XOR for binary, multiplication for real).
fn bind(&self, other: &Self) -> Self;
/// Bundle multiple hypervectors (element-wise majority vote).
fn bundle(vectors: &[&Self]) -> Self
where
Self: Sized;
/// Permute the hypervector (cyclic shift).
fn permute(&self, shift: usize) -> Self;
/// Compute cosine similarity with another hypervector.
fn similarity(&self, other: &Self) -> f32;
/// Normalize to unit length.
fn normalize(&mut self);
}
/// Real-valued hypervector.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Hypervector {
/// Components of the hypervector.
pub components: Vec<f32>,
}
impl Hypervector {
/// Create a new zero hypervector.
pub fn zeros(dim: usize) -> Self {
Self {
components: vec![0.0; dim],
}
}
/// Create a new random hypervector (uniformly distributed).
pub fn random(dim: usize) -> Self {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut components = Vec::with_capacity(dim);
let mut hasher = DefaultHasher::new();
for i in 0..dim {
i.hash(&mut hasher);
let h = hasher.finish();
// Map to [-1, 1]
let value = (h as f32 / u64::MAX as f32) * 2.0 - 1.0;
components.push(value);
hasher = DefaultHasher::new();
h.hash(&mut hasher);
}
Self { components }
}
/// Create from a scalar value.
pub fn from_scalar(value: f32, dim: usize) -> Self {
// Use the scalar to seed a deterministic random generator
let seed = (value * 1000000.0) as u64;
let mut components = Vec::with_capacity(dim);
for i in 0..dim {
// Simple LCG for deterministic generation
let mixed = seed
.wrapping_mul(6364136223846793005)
.wrapping_add(i as u64);
let normalized = (mixed as f32 / u64::MAX as f32) * 2.0 - 1.0;
components.push(normalized);
}
Self { components }
}
/// Create from bytes (e.g., hash).
pub fn from_bytes(bytes: &[u8], dim: usize) -> Self {
let mut components = vec![0.0; dim];
for (i, &b) in bytes.iter().enumerate() {
let idx = i % dim;
// Accumulate byte values into components
components[idx] += (b as f32 / 255.0) * 2.0 - 1.0;
}
let mut hv = Self { components };
hv.normalize();
hv
}
/// Get the dimension.
pub fn dim(&self) -> usize {
self.components.len()
}
/// Compute the L2 norm.
pub fn norm(&self) -> f32 {
self.components.iter().map(|x| x * x).sum::<f32>().sqrt()
}
/// Scale by a scalar.
pub fn scale(&mut self, factor: f32) {
for c in &mut self.components {
*c *= factor;
}
}
/// Add another hypervector.
pub fn add(&mut self, other: &Self) {
for (a, b) in self.components.iter_mut().zip(other.components.iter()) {
*a += b;
}
}
}
impl HypervectorOps for Hypervector {
fn bind(&self, other: &Self) -> Self {
let components: Vec<f32> = self
.components
.iter()
.zip(other.components.iter())
.map(|(a, b)| a * b)
.collect();
Self { components }
}
fn bundle(vectors: &[&Self]) -> Self {
if vectors.is_empty() {
return Self::zeros(DEFAULT_HDC_DIM);
}
let dim = vectors[0].dim();
let mut result = Self::zeros(dim);
for v in vectors {
result.add(v);
}
result.normalize();
result
}
fn permute(&self, shift: usize) -> Self {
let n = self.components.len();
let shift = shift % n;
let mut components = vec![0.0; n];
for i in 0..n {
components[(i + shift) % n] = self.components[i];
}
Self { components }
}
fn similarity(&self, other: &Self) -> f32 {
let dot: f32 = self
.components
.iter()
.zip(other.components.iter())
.map(|(a, b)| a * b)
.sum();
let norm_a = self.norm();
let norm_b = other.norm();
if norm_a < 1e-10 || norm_b < 1e-10 {
return 0.0;
}
dot / (norm_a * norm_b)
}
fn normalize(&mut self) {
let norm = self.norm();
if norm > 1e-10 {
for c in &mut self.components {
*c /= norm;
}
}
}
}
/// Encoded witness record as a hypervector.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WitnessEncoding {
/// The hypervector encoding.
pub hypervector: Hypervector,
/// Original witness ID (for reference).
pub witness_id: String,
/// Energy at time of encoding.
pub energy: f32,
/// Decision (allow/deny).
pub allow: bool,
/// Timestamp of encoding.
pub timestamp_ms: u64,
}
impl WitnessEncoding {
/// Create a new witness encoding.
pub fn new(
witness_id: impl Into<String>,
energy: f32,
allow: bool,
policy_hash: &[u8],
dim: usize,
) -> Self {
let witness_id = witness_id.into();
// Create component hypervectors
let energy_hv = Hypervector::from_scalar(energy, dim);
let decision_hv = Hypervector::from_scalar(if allow { 1.0 } else { -1.0 }, dim);
let policy_hv = Hypervector::from_bytes(policy_hash, dim);
// Bind all components
let bound = energy_hv.bind(&decision_hv).bind(&policy_hv);
Self {
hypervector: bound,
witness_id,
energy,
allow,
timestamp_ms: current_time_ms(),
}
}
/// Get similarity to another encoding.
pub fn similarity(&self, other: &Self) -> f32 {
self.hypervector.similarity(&other.hypervector)
}
}
/// HDC memory for storing and retrieving witness encodings.
pub struct HdcMemory {
/// Stored encodings indexed by ID.
encodings: HashMap<String, WitnessEncoding>,
/// Hypervector dimension.
dim: usize,
/// Maximum capacity.
capacity: usize,
}
impl HdcMemory {
/// Create a new HDC memory.
pub fn new(dim: usize, capacity: usize) -> Self {
Self {
encodings: HashMap::with_capacity(capacity),
dim,
capacity,
}
}
/// Store an encoding.
pub fn store(&mut self, encoding: WitnessEncoding) {
// If at capacity, remove oldest
if self.encodings.len() >= self.capacity {
// Find oldest
if let Some(oldest_id) = self
.encodings
.iter()
.min_by_key(|(_, e)| e.timestamp_ms)
.map(|(id, _)| id.clone())
{
self.encodings.remove(&oldest_id);
}
}
self.encodings.insert(encoding.witness_id.clone(), encoding);
}
/// Retrieve encodings similar to a query.
pub fn retrieve(&self, query: &Hypervector, threshold: f32) -> Vec<(String, f32)> {
let mut results: Vec<_> = self
.encodings
.iter()
.map(|(id, enc)| {
let sim = enc.hypervector.similarity(query);
(id.clone(), sim)
})
.filter(|(_, sim)| *sim >= threshold)
.collect();
// Sort by similarity descending
results.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
results
}
/// Get an encoding by ID.
pub fn get(&self, id: &str) -> Option<&WitnessEncoding> {
self.encodings.get(id)
}
/// Get the number of stored encodings.
pub fn len(&self) -> usize {
self.encodings.len()
}
/// Check if empty.
pub fn is_empty(&self) -> bool {
self.encodings.is_empty()
}
/// Clear all encodings.
pub fn clear(&mut self) {
self.encodings.clear();
}
}
impl std::fmt::Debug for HdcMemory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HdcMemory")
.field("dim", &self.dim)
.field("stored", &self.encodings.len())
.field("capacity", &self.capacity)
.finish()
}
}
/// Get current time in milliseconds.
fn current_time_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hypervector_operations() {
let a = Hypervector::random(1000);
let b = Hypervector::random(1000);
// Self-similarity should be ~1
let self_sim = a.similarity(&a);
assert!((self_sim - 1.0).abs() < 0.01);
// Random vectors should be nearly orthogonal
let cross_sim = a.similarity(&b);
assert!(cross_sim.abs() < 0.2);
}
#[test]
fn test_hypervector_bind() {
let a = Hypervector::from_scalar(1.0, 1000);
let b = Hypervector::from_scalar(2.0, 1000);
let bound = a.bind(&b);
assert_eq!(bound.dim(), 1000);
}
#[test]
fn test_witness_encoding() {
let enc = WitnessEncoding::new("test_witness", 0.5, true, &[1, 2, 3, 4], 1000);
assert_eq!(enc.witness_id, "test_witness");
assert!(enc.allow);
}
#[test]
fn test_hdc_memory() {
let mut memory = HdcMemory::new(1000, 100);
let enc = WitnessEncoding::new("w1", 0.5, true, &[1, 2, 3], 1000);
memory.store(enc);
assert_eq!(memory.len(), 1);
assert!(memory.get("w1").is_some());
}
}

View File

@@ -0,0 +1,79 @@
//! Error types for the neural gate integration module.
use thiserror::Error;
/// Result type for neural gate operations.
pub type NeuralGateResult<T> = Result<T, NeuralGateError>;
/// Errors that can occur in neural gate operations.
#[derive(Debug, Error)]
pub enum NeuralGateError {
/// Gate not initialized.
#[error("neural gate not initialized")]
NotInitialized,
/// Invalid energy value.
#[error("invalid energy value: {0}")]
InvalidEnergy(f32),
/// Hysteresis tracking error.
#[error("hysteresis tracking error: {0}")]
HysteresisError(String),
/// Dendritic processing error.
#[error("dendritic processing error: {0}")]
DendriticError(String),
/// Workspace broadcast error.
#[error("workspace broadcast error: {0}")]
WorkspaceError(String),
/// HDC encoding error.
#[error("HDC encoding error: {0}")]
HdcEncodingError(String),
/// Memory retrieval error.
#[error("memory retrieval error: {0}")]
MemoryError(String),
/// Configuration error.
#[error("configuration error: {0}")]
ConfigurationError(String),
/// Dimension mismatch.
#[error("dimension mismatch: expected {expected}, got {actual}")]
DimensionMismatch {
/// Expected dimension.
expected: usize,
/// Actual dimension.
actual: usize,
},
/// Oscillator synchronization error.
#[error("oscillator sync error: {0}")]
OscillatorError(String),
/// Internal error.
#[error("internal neural gate error: {0}")]
Internal(String),
}
impl NeuralGateError {
/// Create a dimension mismatch error.
#[must_use]
pub fn dim_mismatch(expected: usize, actual: usize) -> Self {
Self::DimensionMismatch { expected, actual }
}
/// Create a hysteresis error.
#[must_use]
pub fn hysteresis(msg: impl Into<String>) -> Self {
Self::HysteresisError(msg.into())
}
/// Create a dendritic error.
#[must_use]
pub fn dendritic(msg: impl Into<String>) -> Self {
Self::DendriticError(msg.into())
}
}

View File

@@ -0,0 +1,514 @@
//! Neural coherence gate implementation.
use super::config::NeuralGateConfig;
use super::decision::{DecisionConfidence, DecisionTrigger, HysteresisState, NeuralDecision};
use super::encoding::{HdcMemory, Hypervector, WitnessEncoding};
use std::collections::VecDeque;
/// State of the neural coherence gate.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GateState {
/// Gate is uninitialized.
Uninitialized,
/// Gate is ready.
Ready,
/// Gate is processing.
Processing,
/// Gate is in broadcast mode.
Broadcasting,
}
/// Hysteresis tracker for stable decisions.
#[derive(Debug)]
struct HysteresisTracker {
/// Current state.
state: HysteresisState,
/// Smoothed energy value.
smoothed_energy: f32,
/// Time entered current state.
state_entered_ms: u64,
/// Low threshold.
low_threshold: f32,
/// High threshold.
high_threshold: f32,
/// Minimum dwell time.
min_dwell_ms: u64,
/// Smoothing factor.
smoothing: f32,
}
impl HysteresisTracker {
fn new(config: &super::config::HysteresisConfig) -> Self {
Self {
state: HysteresisState::Low,
smoothed_energy: 0.0,
state_entered_ms: current_time_ms(),
low_threshold: config.low_threshold,
high_threshold: config.high_threshold,
min_dwell_ms: config.min_dwell_time_ms,
smoothing: config.smoothing_factor,
}
}
fn update(&mut self, energy: f32) -> Option<HysteresisState> {
// Apply exponential smoothing
self.smoothed_energy =
self.smoothing * self.smoothed_energy + (1.0 - self.smoothing) * energy;
let now = current_time_ms();
let dwell_time = now - self.state_entered_ms;
// Check if we've dwelled long enough to consider switching
if dwell_time < self.min_dwell_ms {
return None;
}
let old_state = self.state;
// Determine new state based on smoothed energy
let new_state = match self.state {
HysteresisState::Low => {
if self.smoothed_energy > self.high_threshold {
HysteresisState::High
} else if self.smoothed_energy > self.low_threshold {
HysteresisState::Transition
} else {
HysteresisState::Low
}
}
HysteresisState::Transition => {
if self.smoothed_energy > self.high_threshold {
HysteresisState::High
} else if self.smoothed_energy < self.low_threshold {
HysteresisState::Low
} else {
HysteresisState::Transition
}
}
HysteresisState::High => {
if self.smoothed_energy < self.low_threshold {
HysteresisState::Low
} else if self.smoothed_energy < self.high_threshold {
HysteresisState::Transition
} else {
HysteresisState::High
}
}
};
if new_state != old_state {
self.state = new_state;
self.state_entered_ms = now;
Some(new_state)
} else {
None
}
}
}
/// Dendritic coincidence detector.
#[derive(Debug)]
struct DendriticDetector {
/// Active synapses (timestamp of last spike).
synapses: VecDeque<(u64, u64)>, // (synapse_id, timestamp_ms)
/// Coincidence window in ms.
window_ms: u64,
/// Threshold for coincidence detection.
threshold: usize,
}
impl DendriticDetector {
fn new(window_us: u64, threshold: usize) -> Self {
Self {
synapses: VecDeque::with_capacity(100),
window_ms: window_us / 1000,
threshold,
}
}
fn receive_spike(&mut self, synapse_id: u64) {
let now = current_time_ms();
// Remove old spikes
while let Some(&(_, ts)) = self.synapses.front() {
if now - ts > self.window_ms {
self.synapses.pop_front();
} else {
break;
}
}
// Add new spike
self.synapses.push_back((synapse_id, now));
}
fn check_coincidence(&self) -> Option<usize> {
let now = current_time_ms();
// Count unique synapses that fired within window
let active: std::collections::HashSet<u64> = self
.synapses
.iter()
.filter(|(_, ts)| now - ts <= self.window_ms)
.map(|(id, _)| *id)
.collect();
if active.len() >= self.threshold {
Some(active.len())
} else {
None
}
}
fn clear(&mut self) {
self.synapses.clear();
}
}
/// Global workspace for conscious access.
#[derive(Debug)]
struct GlobalWorkspace {
/// Buffer of recent decisions.
buffer: VecDeque<NeuralDecision>,
/// Capacity.
capacity: usize,
/// Broadcast threshold.
broadcast_threshold: f32,
/// Broadcast listeners (count).
listener_count: usize,
}
impl GlobalWorkspace {
fn new(config: &super::config::WorkspaceConfig) -> Self {
Self {
buffer: VecDeque::with_capacity(config.buffer_capacity),
capacity: config.buffer_capacity,
broadcast_threshold: config.broadcast_threshold,
listener_count: 0,
}
}
fn broadcast(&mut self, decision: NeuralDecision) {
if self.buffer.len() >= self.capacity {
self.buffer.pop_front();
}
self.buffer.push_back(decision);
self.listener_count += 1; // Simulate notification
}
fn recent_decisions(&self, count: usize) -> Vec<&NeuralDecision> {
self.buffer.iter().rev().take(count).collect()
}
fn should_broadcast(&self, confidence: f32) -> bool {
confidence >= self.broadcast_threshold
}
}
/// Context for gate evaluation.
#[derive(Debug, Clone)]
pub struct EvaluationContext {
/// Evidence source IDs.
pub evidence_sources: Vec<u64>,
/// Timestamp.
pub timestamp_ms: u64,
/// Additional metadata.
pub metadata: std::collections::HashMap<String, String>,
}
impl EvaluationContext {
/// Create a new context.
pub fn new() -> Self {
Self {
evidence_sources: Vec::new(),
timestamp_ms: current_time_ms(),
metadata: std::collections::HashMap::new(),
}
}
/// Add an evidence source.
pub fn with_evidence(mut self, source_id: u64) -> Self {
self.evidence_sources.push(source_id);
self
}
}
impl Default for EvaluationContext {
fn default() -> Self {
Self::new()
}
}
/// Neural coherence gate using biologically-inspired mechanisms.
pub struct NeuralCoherenceGate {
/// Configuration.
config: NeuralGateConfig,
/// Hysteresis tracker.
hysteresis: HysteresisTracker,
/// Dendritic coincidence detector.
dendrite: DendriticDetector,
/// Global workspace.
workspace: GlobalWorkspace,
/// HDC memory for witness encoding.
hdc_memory: HdcMemory,
/// State.
state: GateState,
/// Total evaluations.
total_evaluations: u64,
}
impl NeuralCoherenceGate {
/// Create a new neural coherence gate.
pub fn new(config: NeuralGateConfig) -> Self {
let hysteresis = HysteresisTracker::new(&config.hysteresis);
let dendrite =
DendriticDetector::new(config.coincidence_window_us, config.num_branches / 2);
let workspace = GlobalWorkspace::new(&config.workspace);
let hdc_memory = HdcMemory::new(config.hdc_dimension, config.memory_capacity);
Self {
config,
hysteresis,
dendrite,
workspace,
hdc_memory,
state: GateState::Ready,
total_evaluations: 0,
}
}
/// Create with default configuration.
pub fn default_gate() -> Self {
Self::new(NeuralGateConfig::default())
}
/// Get the current state.
pub fn state(&self) -> GateState {
self.state
}
/// Evaluate whether to allow an action.
pub fn evaluate(&mut self, energy: f32, context: &EvaluationContext) -> NeuralDecision {
self.state = GateState::Processing;
self.total_evaluations += 1;
// Process evidence through dendritic detector
for &source in &context.evidence_sources {
self.dendrite.receive_spike(source);
}
// Check for dendritic coincidence
let dendritic_fire = self.dendrite.check_coincidence();
let dendritic_confidence = dendritic_fire
.map(|count| (count as f32 / self.config.num_branches as f32).min(1.0))
.unwrap_or(0.3);
// Update hysteresis
let state_change = self.hysteresis.update(energy);
let hysteresis_state = self.hysteresis.state;
// Determine trigger
let trigger = if let Some(count) = dendritic_fire {
DecisionTrigger::DendriticCoincidence {
active_synapses: count,
threshold: self.config.num_branches / 2,
}
} else if let Some(new_state) = state_change {
DecisionTrigger::HysteresisChange {
from_state: match new_state {
HysteresisState::High => HysteresisState::Transition,
HysteresisState::Low => HysteresisState::Transition,
HysteresisState::Transition => HysteresisState::Low,
},
to_state: new_state,
}
} else {
DecisionTrigger::EnergyThreshold {
threshold: self.hysteresis.low_threshold,
upward: energy > self.hysteresis.smoothed_energy,
}
};
// Compute confidence
let energy_confidence = 1.0 - energy.min(1.0);
let oscillator_confidence = 0.7; // Placeholder
let confidence = DecisionConfidence::new(
energy_confidence,
dendritic_confidence,
oscillator_confidence,
context.evidence_sources.len(),
);
// Make decision
let allow = match hysteresis_state {
HysteresisState::Low => true,
HysteresisState::Transition => confidence.overall > 0.5,
HysteresisState::High => false,
};
let decision = NeuralDecision::new(
allow,
energy,
self.hysteresis.smoothed_energy,
hysteresis_state,
trigger,
confidence,
);
// Broadcast if significant
if decision.should_broadcast && self.workspace.should_broadcast(confidence.overall) {
self.state = GateState::Broadcasting;
self.workspace.broadcast(decision.clone());
}
self.state = GateState::Ready;
decision
}
/// Encode a witness record as a hypervector.
pub fn encode_witness(
&mut self,
witness_id: &str,
energy: f32,
allow: bool,
policy_hash: &[u8],
) -> WitnessEncoding {
let encoding = WitnessEncoding::new(
witness_id,
energy,
allow,
policy_hash,
self.config.hdc_dimension,
);
self.hdc_memory.store(encoding.clone());
encoding
}
/// Find similar past witnesses.
pub fn find_similar_witnesses(&self, query: &Hypervector, threshold: f32) -> Vec<String> {
self.hdc_memory
.retrieve(query, threshold)
.into_iter()
.map(|(id, _)| id)
.collect()
}
/// Get recent decisions from the workspace.
pub fn recent_decisions(&self, count: usize) -> Vec<&NeuralDecision> {
self.workspace.recent_decisions(count)
}
/// Get gate statistics.
pub fn stats(&self) -> GateStats {
GateStats {
state: self.state,
hysteresis_state: self.hysteresis.state,
smoothed_energy: self.hysteresis.smoothed_energy,
total_evaluations: self.total_evaluations,
encoded_witnesses: self.hdc_memory.len(),
}
}
/// Reset the gate.
pub fn reset(&mut self) {
self.hysteresis = HysteresisTracker::new(&self.config.hysteresis);
self.dendrite.clear();
self.hdc_memory.clear();
self.total_evaluations = 0;
self.state = GateState::Ready;
}
}
impl std::fmt::Debug for NeuralCoherenceGate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NeuralCoherenceGate")
.field("state", &self.state)
.field("hysteresis_state", &self.hysteresis.state)
.field("total_evaluations", &self.total_evaluations)
.finish()
}
}
/// Gate statistics.
#[derive(Debug, Clone, Copy)]
pub struct GateStats {
/// Current state.
pub state: GateState,
/// Current hysteresis state.
pub hysteresis_state: HysteresisState,
/// Smoothed energy value.
pub smoothed_energy: f32,
/// Total evaluations.
pub total_evaluations: u64,
/// Number of encoded witnesses.
pub encoded_witnesses: usize,
}
/// Get current time in milliseconds.
fn current_time_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gate_creation() {
let gate = NeuralCoherenceGate::default_gate();
assert_eq!(gate.state(), GateState::Ready);
}
#[test]
fn test_evaluate_low_energy() {
let mut gate = NeuralCoherenceGate::default_gate();
let context = EvaluationContext::new();
let decision = gate.evaluate(0.1, &context);
assert!(decision.allow);
assert_eq!(decision.hysteresis_state, HysteresisState::Low);
}
#[test]
fn test_evaluate_high_energy() {
let mut gate = NeuralCoherenceGate::default_gate();
let context = EvaluationContext::new();
// Need multiple evaluations to move through hysteresis
for _ in 0..10 {
gate.evaluate(0.9, &context);
std::thread::sleep(std::time::Duration::from_millis(20));
}
let decision = gate.evaluate(0.9, &context);
// After sustained high energy, should deny
assert!(!decision.allow || decision.hysteresis_state == HysteresisState::High);
}
#[test]
fn test_witness_encoding() {
let mut gate = NeuralCoherenceGate::default_gate();
let encoding = gate.encode_witness("test", 0.5, true, &[1, 2, 3, 4]);
assert_eq!(encoding.witness_id, "test");
assert!(encoding.allow);
}
#[test]
fn test_find_similar() {
let mut gate = NeuralCoherenceGate::default_gate();
gate.encode_witness("w1", 0.5, true, &[1, 2, 3, 4]);
gate.encode_witness("w2", 0.6, true, &[1, 2, 3, 5]);
let query = Hypervector::from_bytes(&[1, 2, 3, 4], gate.config.hdc_dimension);
let similar = gate.find_similar_witnesses(&query, 0.5);
assert!(!similar.is_empty());
}
}

View File

@@ -0,0 +1,56 @@
//! Neural Gate Integration - ruvector-nervous-system Adapter
//!
//! This module provides biologically-inspired gating using the `ruvector-nervous-system`
//! crate. It implements neural coherence gating with features from neuroscience:
//!
//! - **Dendritic coincidence detection**: Multiple evidence sources must align
//! - **Hysteresis**: Prevents rapid oscillation between states
//! - **Global workspace**: Broadcast mechanism for significant decisions
//! - **HDC encoding**: Hyperdimensional computing for witness similarity
//!
//! # Architecture
//!
//! The neural gate uses oscillatory routing (Kuramoto model) and workspace theory
//! to implement a coherence-gated decision system that:
//!
//! 1. Filters noise through dendritic coincidence detection
//! 2. Maintains stable decisions via hysteresis
//! 3. Broadcasts significant decisions to all modules
//! 4. Encodes witnesses as hypervectors for similarity search
//!
//! # Key Types
//!
//! - [`NeuralCoherenceGate`]: Main neural gating system
//! - [`NeuralDecision`]: Decision from the neural gate
//! - [`WitnessEncoding`]: HDC encoding of witness records
//! - [`NeuralGateConfig`]: Configuration for the neural gate
//!
//! # Example
//!
//! ```rust,ignore
//! use prime_radiant::neural_gate::{NeuralCoherenceGate, NeuralGateConfig};
//!
//! // Create neural gate
//! let mut gate = NeuralCoherenceGate::new(NeuralGateConfig::default());
//!
//! // Evaluate with biologically-inspired gating
//! let decision = gate.evaluate(energy, &context);
//!
//! // Encode witness as hypervector
//! let encoding = gate.encode_witness(&witness_record);
//!
//! // Find similar past witnesses
//! let similar = gate.find_similar_witnesses(&encoding.hypervector, 0.8);
//! ```
mod config;
mod decision;
mod encoding;
mod error;
mod gate;
pub use config::{HysteresisConfig, NeuralGateConfig, OscillatorConfig, WorkspaceConfig};
pub use decision::{DecisionConfidence, DecisionTrigger, NeuralDecision};
pub use encoding::{HypervectorOps, WitnessEncoding};
pub use error::{NeuralGateError, NeuralGateResult};
pub use gate::{GateState, NeuralCoherenceGate};