sc_neurocore_engine/bindings/mckean.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 — McKean neuron PyO3 binding
8
9//! Python binding for the McKean piecewise-linear neuron.
10
11use numpy::{IntoPyArray, PyArray1};
12use pyo3::prelude::*;
13use pyo3::types::PyDict;
14
15use crate::neurons::McKeanNeuron;
16
17py_neuron_default!("McKeanNeuron", PyMcKeanNeuron, McKeanNeuron, state v, state w);
18
19/// Register the McKean class and simulator with the extension module.
20pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
21 module.add_class::<PyMcKeanNeuron>()?;
22 module.add_function(wrap_pyfunction!(py_mckean_simulate, module)?)?;
23 Ok(())
24}
25
26/// N-step McKean (1970) piecewise-linear FitzHugh-Nagumo caricature simulation.
27///
28/// Parity contract with
29/// `sc_neurocore.neurons.models.mckean.McKeanNeuron.simulate`: for the same
30/// parameters and constant input the returned `v` trace, upward-`v_peak`-crossing
31/// spike count, and final `(v, w)` state are bit-identical to the Python RK4
32/// reference (the piecewise-linear right-hand side is exact arithmetic —
33/// additions, multiplications and branch selection, no transcendental functions —
34/// and a two-dimensional autonomous flow cannot be chaotic).
35#[pyfunction]
36#[pyo3(signature = (v0, w0, a, epsilon, gamma, dt, v_peak, n_steps, current))]
37#[allow(clippy::too_many_arguments)]
38fn py_mckean_simulate<'py>(
39 py: Python<'py>,
40 v0: f64,
41 w0: f64,
42 a: f64,
43 epsilon: f64,
44 gamma: f64,
45 dt: f64,
46 v_peak: f64,
47 n_steps: usize,
48 current: f64,
49) -> (Bound<'py, PyArray1<f64>>, i64, f64, f64) {
50 let mut neuron = McKeanNeuron {
51 v: v0,
52 w: w0,
53 a,
54 epsilon,
55 gamma,
56 dt,
57 v_peak,
58 };
59 let (trace, spikes) = neuron.simulate(n_steps, current);
60 (trace.into_pyarray(py), spikes, neuron.v, neuron.w)
61}