terminals-core 0.1.0

Core runtime primitives for Terminals OS: phase dynamics, AXON wire protocol, substrate engine, and sematonic types
Documentation
//! KuramotoProjection — Phase oscillator state.
//!
//! Each atom carries a phase θ, natural frequency ω, and coupling strength K.
//! These feed directly into the Kuramoto model via the existing
//! `phase::kuramoto::kuramoto_step()`.

use super::projection::{Projection, ProjectionId};

/// 3 floats: theta, omega, coupling = 12 bytes.
const KURAMOTO_BYTES: usize = 3 * 4;

#[derive(Debug, Clone, Copy)]
pub struct KuramotoProjection {
    /// Phase angle θ in radians.
    pub theta: f32,
    /// Natural frequency ω (rad/s).
    pub omega: f32,
    /// Per-atom coupling strength K.
    pub coupling: f32,
}

impl Default for KuramotoProjection {
    fn default() -> Self {
        Self {
            theta: 0.0,
            omega: 0.0,
            coupling: 1.0,
        }
    }
}

impl Projection for KuramotoProjection {
    fn byte_size() -> usize {
        KURAMOTO_BYTES
    }

    fn id() -> ProjectionId {
        ProjectionId::Kuramoto
    }

    fn read(buf: &[u8]) -> Self {
        assert!(buf.len() >= KURAMOTO_BYTES);
        Self {
            theta: f32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]),
            omega: f32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]),
            coupling: f32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]),
        }
    }

    fn write(&self, buf: &mut [u8]) {
        assert!(buf.len() >= KURAMOTO_BYTES);
        buf[0..4].copy_from_slice(&self.theta.to_le_bytes());
        buf[4..8].copy_from_slice(&self.omega.to_le_bytes());
        buf[8..12].copy_from_slice(&self.coupling.to_le_bytes());
    }

    fn shape_hash_contribution(&self) -> u32 {
        let mut hash = 0x811c_9dc5u32;
        for byte in self.theta.to_bits().to_le_bytes() {
            hash ^= byte as u32;
            hash = hash.wrapping_mul(0x0100_0193);
        }
        hash
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::f32::consts::PI;

    #[test]
    fn test_kuramoto_byte_size() {
        assert_eq!(KuramotoProjection::byte_size(), 12);
    }

    #[test]
    fn test_kuramoto_roundtrip() {
        let proj = KuramotoProjection {
            theta: PI / 4.0,
            omega: 1.5,
            coupling: 2.0,
        };
        let mut buf = vec![0u8; KuramotoProjection::byte_size()];
        proj.write(&mut buf);
        let restored = KuramotoProjection::read(&buf);
        assert!((restored.theta - PI / 4.0).abs() < 1e-6);
        assert!((restored.omega - 1.5).abs() < 1e-6);
        assert!((restored.coupling - 2.0).abs() < 1e-6);
    }
}