Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
212
vendor/ruvector/crates/prime-radiant/src/neural_gate/config.rs
vendored
Normal file
212
vendor/ruvector/crates/prime-radiant/src/neural_gate/config.rs
vendored
Normal 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());
|
||||
}
|
||||
}
|
||||
248
vendor/ruvector/crates/prime-radiant/src/neural_gate/decision.rs
vendored
Normal file
248
vendor/ruvector/crates/prime-radiant/src/neural_gate/decision.rs
vendored
Normal 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());
|
||||
}
|
||||
}
|
||||
379
vendor/ruvector/crates/prime-radiant/src/neural_gate/encoding.rs
vendored
Normal file
379
vendor/ruvector/crates/prime-radiant/src/neural_gate/encoding.rs
vendored
Normal 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());
|
||||
}
|
||||
}
|
||||
79
vendor/ruvector/crates/prime-radiant/src/neural_gate/error.rs
vendored
Normal file
79
vendor/ruvector/crates/prime-radiant/src/neural_gate/error.rs
vendored
Normal 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())
|
||||
}
|
||||
}
|
||||
514
vendor/ruvector/crates/prime-radiant/src/neural_gate/gate.rs
vendored
Normal file
514
vendor/ruvector/crates/prime-radiant/src/neural_gate/gate.rs
vendored
Normal 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());
|
||||
}
|
||||
}
|
||||
56
vendor/ruvector/crates/prime-radiant/src/neural_gate/mod.rs
vendored
Normal file
56
vendor/ruvector/crates/prime-radiant/src/neural_gate/mod.rs
vendored
Normal 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};
|
||||
Reference in New Issue
Block a user