sc_neurocore_engine/bindings/terman_wang.rs
1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// SC-NeuroCore — Terman-Wang oscillator PyO3 binding
8
9//! Python binding for the Terman-Wang LEGION relaxation oscillator.
10
11use numpy::{IntoPyArray, PyArray1};
12use pyo3::prelude::*;
13use pyo3::types::PyDict;
14
15use crate::neurons::TermanWangOscillator;
16
17py_neuron_default!("TermanWangOscillator", PyTermanWangOscillator, TermanWangOscillator, state v, state w);
18
19/// Register the Terman-Wang class and simulator with the extension module.
20pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
21 module.add_class::<PyTermanWangOscillator>()?;
22 module.add_function(wrap_pyfunction!(py_terman_wang_simulate, module)?)?;
23 Ok(())
24}
25
26/// N-step Terman-Wang (LEGION) relaxation-oscillator simulation.
27///
28/// Parity contract with
29/// `sc_neurocore.neurons.models.terman_wang.TermanWangOscillator.simulate`: for
30/// the same parameters and constant input the returned `v` trace, upward-crossing
31/// spike count, and final `(v, w)` state match the Python RK4 reference. The cubic
32/// uses `v.powi(3)` = `v*v*v` (matching the Python `v*v*v`); the `tanh` gating
33/// resolves to the same glibc symbol as Python on Linux, so this backend is
34/// bit-identical there (Julia/Go/Mojo use their own libm `tanh`, ULP-bounded, and
35/// the two-dimensional relaxation oscillator is non-chaotic so it does not amplify).
36#[pyfunction]
37#[pyo3(signature = (v0, w0, alpha, beta, epsilon, rho, dt, v_peak, n_steps, current))]
38#[allow(clippy::too_many_arguments)]
39fn py_terman_wang_simulate<'py>(
40 py: Python<'py>,
41 v0: f64,
42 w0: f64,
43 alpha: f64,
44 beta: f64,
45 epsilon: f64,
46 rho: f64,
47 dt: f64,
48 v_peak: f64,
49 n_steps: usize,
50 current: f64,
51) -> (Bound<'py, PyArray1<f64>>, i64, f64, f64) {
52 let mut neuron = TermanWangOscillator {
53 v: v0,
54 w: w0,
55 alpha,
56 beta,
57 epsilon,
58 rho,
59 dt,
60 v_peak,
61 };
62 let (trace, spikes) = neuron.simulate(n_steps, current);
63 (trace.into_pyarray(py), spikes, neuron.v, neuron.w)
64}