use std::collections::HashSet;
use crate::arena::{LineageId, PsycheArena};
use crate::graph::BondGraph;
use crate::setun::Trit;
pub const DEFAULT_RESISTANCE: f32 = 0.5;
pub const DEFAULT_CUTOFF: f32 = 0.1;
#[derive(Debug, Clone)]
pub struct SynapseConfig {
pub resistance: f32,
pub cutoff: f32,
pub max_depth: usize,
}
impl Default for SynapseConfig {
fn default() -> Self {
Self {
resistance: DEFAULT_RESISTANCE,
cutoff: DEFAULT_CUTOFF,
max_depth: 10, }
}
}
pub struct SynapseEngine {
config: SynapseConfig,
}
impl SynapseEngine {
pub fn new() -> Self {
Self::with_config(SynapseConfig::default())
}
pub fn with_config(config: SynapseConfig) -> Self {
Self { config }
}
pub fn propagate(
&self,
psyche: &mut PsycheArena,
bonds: &BondGraph,
source: LineageId,
input_energy: f32,
) -> usize {
let mut visited = HashSet::new();
self.propagate_recursive(psyche, bonds, source, input_energy, &mut visited, 0)
}
fn propagate_recursive(
&self,
psyche: &mut PsycheArena,
bonds: &BondGraph,
source: LineageId,
input_energy: f32,
visited: &mut HashSet<LineageId>,
depth: usize,
) -> usize {
if input_energy.abs() < self.config.cutoff {
return 0;
}
if depth >= self.config.max_depth {
return 0;
}
if visited.contains(&source) {
return 0;
}
visited.insert(source);
let mut affected = 0;
let neighbor_ids: Vec<_> = bonds.neighbors(source).to_vec();
for bond_id in neighbor_ids {
if let Some(bond) = bonds.get(bond_id) {
if !bond.is_active() {
continue;
}
let polarity_weight = match bond.polarity {
Trit::True => 1.0, Trit::Unknown => 0.0, Trit::False => -1.0, };
if polarity_weight == 0.0 {
continue;
}
let transfer = input_energy * bond.strength * polarity_weight;
let decayed = transfer * (1.0 - self.config.resistance);
let target = bond.other(source);
if let Some(lineage) = psyche.get_mut(target) {
lineage.stimulate(decayed);
affected += 1;
affected += self.propagate_recursive(
psyche,
bonds,
target,
decayed,
visited,
depth + 1,
);
}
}
}
affected
}
}
impl Default for SynapseEngine {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arena::Lineage;
use crate::graph::Bond;
fn setup_chain() -> (PsycheArena, BondGraph) {
let mut psyche = PsycheArena::with_capacity(10);
let mut bonds = BondGraph::with_capacity(10, 100);
let a = psyche.alloc(Lineage::new(0.5));
let b = psyche.alloc(Lineage::new(0.1));
let c = psyche.alloc(Lineage::new(0.1));
let mut bond_ab = Bond::new(a, b, 1.0);
bond_ab.polarity = Trit::True;
bonds.connect(bond_ab);
let mut bond_bc = Bond::new(b, c, 1.0);
bond_bc.polarity = Trit::True;
bonds.connect(bond_bc);
(psyche, bonds)
}
#[test]
fn test_synergy_propagation() {
let (mut psyche, bonds) = setup_chain();
let engine = SynapseEngine::new();
let a = LineageId(0);
let affected = engine.propagate(&mut psyche, &bonds, a, 1.0);
assert!(affected >= 1);
let b = psyche.get(LineageId(1)).unwrap();
assert!(b.energy > 0.1);
let c = psyche.get(LineageId(2)).unwrap();
assert!(c.energy > 0.1);
}
#[test]
fn test_neutral_insulation() {
let mut psyche = PsycheArena::with_capacity(10);
let mut bonds = BondGraph::with_capacity(10, 100);
let a = psyche.alloc(Lineage::new(0.5));
let b = psyche.alloc(Lineage::new(0.1));
let mut bond = Bond::new(a, b, 1.0);
bond.polarity = Trit::Unknown;
bonds.connect(bond);
let engine = SynapseEngine::new();
let affected = engine.propagate(&mut psyche, &bonds, a, 1.0);
assert_eq!(affected, 0);
}
#[test]
fn test_antagonism_inhibition() {
let mut psyche = PsycheArena::with_capacity(10);
let mut bonds = BondGraph::with_capacity(10, 100);
let a = psyche.alloc(Lineage::new(0.5));
let b = psyche.alloc(Lineage::new(0.8));
let mut bond = Bond::new(a, b, 1.0);
bond.polarity = Trit::False;
bonds.connect(bond);
let engine = SynapseEngine::new();
let b_energy_before = psyche.get(LineageId(1)).unwrap().energy;
engine.propagate(&mut psyche, &bonds, a, 1.0);
let b_energy_after = psyche.get(LineageId(1)).unwrap().energy;
assert!(b_energy_after < b_energy_before);
}
#[test]
fn test_cutoff_stops_propagation() {
let (mut psyche, bonds) = setup_chain();
let engine = SynapseEngine::new();
let affected = engine.propagate(&mut psyche, &bonds, LineageId(0), 0.05);
assert_eq!(affected, 0);
}
}