#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct NeuralCircuitMap {
pub x: f64,
pub y: f64,
pub z: f64,
pub a: f64,
pub b: f64,
pub c: f64,
}
impl NeuralCircuitMap {
pub const fn new(x: f64, y: f64, z: f64, a: f64, b: f64, c: f64) -> Self {
Self { x, y, z, a, b, c }
}
#[inline]
pub fn next(&mut self) {
let x_next = f64::tanh(self.a * self.y - self.b * self.x + self.z);
let y_next = f64::tanh(self.b * self.z - self.c * self.y + self.x);
let z_next = f64::tanh(self.c * self.x - self.a * self.z + self.y);
self.x = x_next;
self.y = y_next;
self.z = z_next;
}
#[inline]
pub fn next_value(&mut self) -> f64 {
self.next();
let mut h =
self.x.to_bits() ^ self.y.to_bits().rotate_left(21) ^ self.z.to_bits().rotate_left(42);
h ^= h >> 33;
h = h.wrapping_mul(0xff51afd7ed558ccd);
h ^= h >> 33;
h = h.wrapping_mul(0xc4ceb9fe1a85ec53);
h ^= h >> 33;
#[allow(clippy::cast_precision_loss)]
let result = (h >> 11) as f64 / (1u64 << 53) as f64;
result
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use super::*;
#[test]
fn test_neural_circuit_range() {
let mut map = NeuralCircuitMap::new(0.1, 0.2, 0.3, 2.5, 3.0, 1.5);
for _ in 0..1000 {
let v = map.next_value();
assert!(v >= 0.0 && v < 1.0, "Value {} out of range", v);
}
}
#[test]
fn test_neural_circuit_determinism() {
let mut map1 = NeuralCircuitMap::new(0.1, 0.2, 0.3, 2.5, 3.0, 1.5);
let mut map2 = NeuralCircuitMap::new(0.1, 0.2, 0.3, 2.5, 3.0, 1.5);
for _ in 0..100 {
assert_eq!(map1.next_value(), map2.next_value());
}
}
#[test]
fn test_neural_circuit_sensitivity() {
let mut map1 = NeuralCircuitMap::new(0.1, 0.2, 0.3, 2.5, 3.0, 1.5);
let mut map2 = NeuralCircuitMap::new(0.1000000001, 0.2, 0.3, 2.5, 3.0, 1.5);
for _ in 0..1000 {
map1.next();
map2.next();
}
assert!((map1.x - map2.x).abs() >= 0.0 || (map1.y - map2.y).abs() >= 0.0);
}
}