use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, RwLock};
use uuid::Uuid;
use super::code_graph::{CodeBlock, CodeGraph};
use crate::core::HopeResult;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WaveType {
Impulse,
Sine,
Square,
Gaussian,
Spike,
Echo,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WaveState {
Active,
Decaying,
Interfering,
Resonating,
Dead,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Wave {
pub id: String,
pub wave_type: WaveType,
pub payload: String,
pub amplitude: f64,
pub frequency: f64,
pub phase: f64,
pub velocity: f64,
pub decay: f64,
pub origin_id: String,
pub current_positions: HashSet<String>,
pub visited: HashSet<String>,
pub state: WaveState,
pub created_at: DateTime<Utc>,
pub ticks: u64,
pub metadata: HashMap<String, String>,
}
impl Wave {
pub fn new(
origin_id: impl Into<String>,
payload: impl Into<String>,
wave_type: WaveType,
) -> Self {
let origin = origin_id.into();
let mut positions = HashSet::new();
positions.insert(origin.clone());
Self {
id: Uuid::new_v4().to_string(),
wave_type,
payload: payload.into(),
amplitude: 1.0,
frequency: 1.0,
phase: 0.0,
velocity: 1.0,
decay: 0.1,
origin_id: origin.clone(),
current_positions: positions,
visited: {
let mut v = HashSet::new();
v.insert(origin);
v
},
state: WaveState::Active,
created_at: Utc::now(),
ticks: 0,
metadata: HashMap::new(),
}
}
pub fn with_amplitude(mut self, amplitude: f64) -> Self {
self.amplitude = amplitude.clamp(0.0, 1.0);
self
}
pub fn with_frequency(mut self, frequency: f64) -> Self {
self.frequency = frequency.max(0.01);
self
}
pub fn with_velocity(mut self, velocity: f64) -> Self {
self.velocity = velocity.max(0.1);
self
}
pub fn with_decay(mut self, decay: f64) -> Self {
self.decay = decay.clamp(0.0, 1.0);
self
}
pub fn with_meta(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
pub fn current_value(&self) -> f64 {
let phase_offset = self.frequency * self.ticks as f64 * 2.0 * std::f64::consts::PI;
match self.wave_type {
WaveType::Impulse => {
if self.ticks == 0 {
self.amplitude
} else {
0.0
}
}
WaveType::Sine => self.amplitude * (phase_offset + self.phase).sin(),
WaveType::Square => {
if (phase_offset + self.phase).sin() >= 0.0 {
self.amplitude
} else {
-self.amplitude
}
}
WaveType::Gaussian => {
let t = self.ticks as f64;
let sigma = 3.0;
self.amplitude * (-t * t / (2.0 * sigma * sigma)).exp()
}
WaveType::Spike => {
if self.ticks == 0 {
self.amplitude
} else {
self.amplitude * (-(self.ticks as f64) * 0.5).exp()
}
}
WaveType::Echo => {
let decay_factor = (-(self.ticks as f64) * 0.1).exp();
self.amplitude * decay_factor * (phase_offset).sin()
}
}
}
pub fn is_alive(&self) -> bool {
self.amplitude > 0.01 && self.state != WaveState::Dead
}
pub fn apply_decay(&mut self) {
self.amplitude *= 1.0 - self.decay;
if self.amplitude < 0.01 {
self.state = WaveState::Dead;
} else if self.amplitude < 0.3 {
self.state = WaveState::Decaying;
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ActivationFn {
Sigmoid,
ReLU,
Tanh,
Step,
Linear,
Softplus,
}
impl ActivationFn {
pub fn apply(&self, x: f64) -> f64 {
match self {
Self::Sigmoid => 1.0 / (1.0 + (-x).exp()),
Self::ReLU => x.max(0.0),
Self::Tanh => x.tanh(),
Self::Step => {
if x > 0.0 {
1.0
} else {
0.0
}
}
Self::Linear => x,
Self::Softplus => (1.0 + x.exp()).ln(),
}
}
pub fn derivative(&self, x: f64) -> f64 {
match self {
Self::Sigmoid => {
let s = self.apply(x);
s * (1.0 - s)
}
Self::ReLU => {
if x > 0.0 {
1.0
} else {
0.0
}
}
Self::Tanh => 1.0 - x.tanh().powi(2),
Self::Step => 0.0,
Self::Linear => 1.0,
Self::Softplus => 1.0 / (1.0 + (-x).exp()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeuronState {
pub node_id: String,
pub input_sum: f64,
pub activation: f64,
pub threshold: f64,
pub activation_fn: ActivationFn,
pub refractory_ticks: u64,
pub last_fire_tick: Option<u64>,
pub accumulated_waves: f64,
pub wave_count: u64,
}
impl NeuronState {
pub fn new(node_id: impl Into<String>) -> Self {
Self {
node_id: node_id.into(),
input_sum: 0.0,
activation: 0.0,
threshold: 0.5,
activation_fn: ActivationFn::Sigmoid,
refractory_ticks: 0,
last_fire_tick: None,
accumulated_waves: 0.0,
wave_count: 0,
}
}
pub fn with_threshold(mut self, threshold: f64) -> Self {
self.threshold = threshold;
self
}
pub fn with_activation(mut self, activation_fn: ActivationFn) -> Self {
self.activation_fn = activation_fn;
self
}
pub fn receive_wave(&mut self, wave: &Wave) {
let value = wave.current_value().abs();
self.accumulated_waves += value;
self.wave_count += 1;
self.input_sum += value;
}
pub fn compute_activation(&mut self) -> f64 {
self.activation = self.activation_fn.apply(self.input_sum);
self.activation
}
pub fn should_fire(&self) -> bool {
self.activation > self.threshold && self.refractory_ticks == 0
}
pub fn fire(&mut self, current_tick: u64) {
self.last_fire_tick = Some(current_tick);
self.refractory_ticks = 3; self.input_sum = 0.0;
self.accumulated_waves = 0.0;
self.wave_count = 0;
}
pub fn tick(&mut self) {
if self.refractory_ticks > 0 {
self.refractory_ticks -= 1;
}
self.input_sum *= 0.9;
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Interference {
pub node_id: String,
pub wave_ids: Vec<String>,
pub resultant_amplitude: f64,
pub is_constructive: bool,
pub timestamp: DateTime<Utc>,
}
impl Interference {
pub fn compute(node_id: &str, waves: &[&Wave]) -> Self {
let wave_ids: Vec<String> = waves.iter().map(|w| w.id.clone()).collect();
let total_amplitude: f64 = waves.iter().map(|w| w.current_value()).sum();
let avg_amplitude: f64 =
waves.iter().map(|w| w.amplitude).sum::<f64>() / waves.len() as f64;
let is_constructive = total_amplitude.abs() > avg_amplitude;
Self {
node_id: node_id.to_string(),
wave_ids,
resultant_amplitude: total_amplitude,
is_constructive,
timestamp: Utc::now(),
}
}
}
pub struct NeuroGraph {
pub graph: CodeGraph,
neurons: Arc<RwLock<HashMap<String, NeuronState>>>,
waves: Arc<RwLock<HashMap<String, Wave>>>,
interferences: Arc<RwLock<Vec<Interference>>>,
tick: Arc<RwLock<u64>>,
pub learning_rate: f64,
}
impl NeuroGraph {
pub fn new() -> Self {
Self {
graph: CodeGraph::new(),
neurons: Arc::new(RwLock::new(HashMap::new())),
waves: Arc::new(RwLock::new(HashMap::new())),
interferences: Arc::new(RwLock::new(Vec::new())),
tick: Arc::new(RwLock::new(0)),
learning_rate: 0.1,
}
}
pub fn from_graph(graph: CodeGraph) -> Self {
let mut ng = Self {
graph,
neurons: Arc::new(RwLock::new(HashMap::new())),
waves: Arc::new(RwLock::new(HashMap::new())),
interferences: Arc::new(RwLock::new(Vec::new())),
tick: Arc::new(RwLock::new(0)),
learning_rate: 0.1,
};
ng.init_neurons();
ng
}
fn init_neurons(&mut self) {
let blocks = self.graph.all_blocks();
let mut neurons = self.neurons.write().unwrap();
for block in blocks {
let neuron = NeuronState::new(&block.id).with_threshold(0.5 * block.importance);
neurons.insert(block.id, neuron);
}
}
pub fn add_block(&self, block: CodeBlock) -> HopeResult<String> {
let id = self.graph.add(block.clone())?;
let mut neurons = self.neurons.write().unwrap();
let neuron = NeuronState::new(&id).with_threshold(0.5 * block.importance);
neurons.insert(id.clone(), neuron);
Ok(id)
}
pub fn emit_wave(&self, origin_id: &str, payload: &str, wave_type: WaveType) -> Option<String> {
self.graph.get(origin_id)?;
let wave = Wave::new(origin_id, payload, wave_type);
let wave_id = wave.id.clone();
let mut waves = self.waves.write().unwrap();
waves.insert(wave_id.clone(), wave);
Some(wave_id)
}
pub fn emit_wave_custom(&self, wave: Wave) -> Option<String> {
self.graph.get(&wave.origin_id)?;
let wave_id = wave.id.clone();
let mut waves = self.waves.write().unwrap();
waves.insert(wave_id.clone(), wave);
Some(wave_id)
}
pub fn tick(&self) -> TickResult {
let mut result = TickResult::default();
{
let mut tick = self.tick.write().unwrap();
*tick += 1;
result.tick = *tick;
}
let current_tick = result.tick;
{
let mut neurons = self.neurons.write().unwrap();
for neuron in neurons.values_mut() {
neuron.tick();
}
}
let mut new_positions: HashMap<String, HashSet<String>> = HashMap::new();
let mut dead_waves: Vec<String> = Vec::new();
let mut node_waves: HashMap<String, Vec<String>> = HashMap::new();
{
let waves = self.waves.read().unwrap();
for (wave_id, wave) in waves.iter() {
if !wave.is_alive() {
dead_waves.push(wave_id.clone());
continue;
}
let mut next_positions = HashSet::new();
for pos in &wave.current_positions {
if let Some(block) = self.graph.get(pos) {
for conn in &block.connections {
if !wave.visited.contains(&conn.target_id) {
if conn.strength >= wave.amplitude * 0.5 {
next_positions.insert(conn.target_id.clone());
node_waves
.entry(conn.target_id.clone())
.or_default()
.push(wave_id.clone());
}
}
}
}
}
if !next_positions.is_empty() {
new_positions.insert(wave_id.clone(), next_positions);
result.propagations += 1;
}
}
}
{
let mut waves = self.waves.write().unwrap();
for (wave_id, positions) in new_positions {
if let Some(wave) = waves.get_mut(&wave_id) {
wave.visited.extend(positions.iter().cloned());
wave.current_positions = positions;
wave.ticks += 1;
wave.apply_decay();
}
}
for wave_id in dead_waves {
waves.remove(&wave_id);
result.waves_died += 1;
}
result.active_waves = waves.len();
}
{
let waves = self.waves.read().unwrap();
let mut neurons = self.neurons.write().unwrap();
let mut interferences = self.interferences.write().unwrap();
for (node_id, wave_ids) in node_waves {
let arriving_waves: Vec<&Wave> =
wave_ids.iter().filter_map(|wid| waves.get(wid)).collect();
if arriving_waves.len() > 1 {
let interference = Interference::compute(&node_id, &arriving_waves);
result.interferences += 1;
if interference.is_constructive {
result.constructive += 1;
} else {
result.destructive += 1;
}
interferences.push(interference.clone());
if let Some(neuron) = neurons.get_mut(&node_id) {
neuron.input_sum += interference.resultant_amplitude;
}
} else if arriving_waves.len() == 1 {
if let Some(neuron) = neurons.get_mut(&node_id) {
neuron.receive_wave(arriving_waves[0]);
}
}
}
for neuron in neurons.values_mut() {
neuron.compute_activation();
if neuron.should_fire() {
neuron.fire(current_tick);
result.neurons_fired += 1;
}
}
}
result
}
pub fn run(&self, ticks: u64) -> Vec<TickResult> {
(0..ticks).map(|_| self.tick()).collect()
}
pub fn run_until_calm(&self, max_ticks: u64) -> Vec<TickResult> {
let mut results = Vec::new();
for _ in 0..max_ticks {
let result = self.tick();
let done = result.active_waves == 0;
results.push(result);
if done {
break;
}
}
results
}
pub fn active_wave_count(&self) -> usize {
self.waves.read().unwrap().len()
}
pub fn get_waves(&self) -> Vec<Wave> {
self.waves.read().unwrap().values().cloned().collect()
}
pub fn get_neuron(&self, node_id: &str) -> Option<NeuronState> {
self.neurons.read().unwrap().get(node_id).cloned()
}
pub fn get_neurons(&self) -> Vec<NeuronState> {
self.neurons.read().unwrap().values().cloned().collect()
}
pub fn most_active_neurons(&self, limit: usize) -> Vec<NeuronState> {
let neurons = self.neurons.read().unwrap();
let mut sorted: Vec<_> = neurons.values().cloned().collect();
sorted.sort_by(|a, b| b.activation.partial_cmp(&a.activation).unwrap());
sorted.truncate(limit);
sorted
}
pub fn get_interferences(&self) -> Vec<Interference> {
self.interferences.read().unwrap().clone()
}
pub fn current_tick(&self) -> u64 {
*self.tick.read().unwrap()
}
pub fn think(&self, thought: &str, importance: f64) -> Option<String> {
let block_id = self.graph.think(thought, importance).ok()?;
{
let mut neurons = self.neurons.write().unwrap();
neurons.insert(block_id.clone(), NeuronState::new(&block_id));
}
self.emit_wave(&block_id, thought, WaveType::Spike)
}
pub fn remember(&self, memory: &str, importance: f64) -> Option<String> {
let block_id = self.graph.remember(memory, importance).ok()?;
{
let mut neurons = self.neurons.write().unwrap();
neurons.insert(block_id.clone(), NeuronState::new(&block_id));
}
self.emit_wave(&block_id, memory, WaveType::Gaussian)
}
pub fn feel(&self, emotion: &str, intensity: f64) -> Option<String> {
let block_id = self.graph.feel(emotion, intensity, None).ok()?;
{
let mut neurons = self.neurons.write().unwrap();
neurons.insert(block_id.clone(), NeuronState::new(&block_id));
}
let wave = Wave::new(&block_id, emotion, WaveType::Sine)
.with_amplitude(intensity)
.with_frequency(0.5)
.with_decay(0.05);
self.emit_wave_custom(wave)
}
pub fn stats(&self) -> NeuroStats {
let neurons = self.neurons.read().unwrap();
let waves = self.waves.read().unwrap();
let interferences = self.interferences.read().unwrap();
let graph_stats = self.graph.stats();
NeuroStats {
total_neurons: neurons.len(),
active_waves: waves.len(),
total_interferences: interferences.len(),
constructive_interferences: interferences.iter().filter(|i| i.is_constructive).count(),
current_tick: *self.tick.read().unwrap(),
graph_stats,
}
}
}
impl Default for NeuroGraph {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default)]
pub struct TickResult {
pub tick: u64,
pub active_waves: usize,
pub propagations: usize,
pub waves_died: usize,
pub interferences: usize,
pub constructive: usize,
pub destructive: usize,
pub neurons_fired: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NeuroStats {
pub total_neurons: usize,
pub active_waves: usize,
pub total_interferences: usize,
pub constructive_interferences: usize,
pub current_tick: u64,
pub graph_stats: super::code_graph::GraphStats,
}
#[cfg(test)]
mod tests {
use super::super::code_graph::{BlockType, ConnectionType};
use super::*;
#[test]
fn test_wave_creation() {
let wave = Wave::new("node1", "test payload", WaveType::Impulse)
.with_amplitude(0.8)
.with_decay(0.1);
assert_eq!(wave.amplitude, 0.8);
assert!(wave.is_alive());
}
#[test]
fn test_wave_decay() {
let mut wave = Wave::new("node1", "test", WaveType::Impulse)
.with_amplitude(0.5)
.with_decay(0.5);
wave.apply_decay();
assert!(wave.amplitude < 0.5);
}
#[test]
fn test_activation_functions() {
assert!((ActivationFn::Sigmoid.apply(0.0) - 0.5).abs() < 0.001);
assert_eq!(ActivationFn::ReLU.apply(-1.0), 0.0);
assert_eq!(ActivationFn::ReLU.apply(1.0), 1.0);
assert_eq!(ActivationFn::Step.apply(0.5), 1.0);
assert_eq!(ActivationFn::Step.apply(-0.5), 0.0);
}
#[test]
fn test_neurograph_wave_propagation() {
let ng = NeuroGraph::new();
let id1 = ng
.add_block(CodeBlock::new("n1", "p", BlockType::Data, "c"))
.unwrap();
let id2 = ng
.add_block(CodeBlock::new("n2", "p", BlockType::Data, "c"))
.unwrap();
let id3 = ng
.add_block(CodeBlock::new("n3", "p", BlockType::Data, "c"))
.unwrap();
ng.graph
.connect(&id1, &id2, ConnectionType::ConnectsTo, 1.0);
ng.graph
.connect(&id2, &id3, ConnectionType::ConnectsTo, 1.0);
ng.emit_wave(&id1, "test signal", WaveType::Impulse);
let results = ng.run_until_calm(10);
assert!(!results.is_empty());
assert!(results.iter().any(|r| r.propagations > 0));
}
#[test]
fn test_interference() {
let wave1 = Wave::new("n1", "w1", WaveType::Sine).with_amplitude(0.5);
let wave2 = Wave::new("n2", "w2", WaveType::Sine).with_amplitude(0.5);
let interference = Interference::compute("target", &[&wave1, &wave2]);
assert_eq!(interference.wave_ids.len(), 2);
}
#[test]
fn test_neuron_firing() {
let mut neuron = NeuronState::new("test").with_threshold(0.7);
neuron.input_sum = 0.3;
neuron.compute_activation();
assert!(!neuron.should_fire());
neuron.input_sum = 2.0;
neuron.compute_activation();
assert!(neuron.should_fire());
}
#[test]
fn test_think_and_propagate() {
let ng = NeuroGraph::new();
let wave_id = ng.think("This is a thought", 0.8);
assert!(wave_id.is_some());
let stats = ng.stats();
assert_eq!(stats.active_waves, 1);
}
}