Skip to main content

sc_neurocore_engine/bindings/
fitzhugh_rinzel.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 — FitzHugh-Rinzel PyO3 binding
8
9//! Python binding for the FitzHugh-Rinzel three-timescale bursting model.
10
11use numpy::{IntoPyArray, PyArray1};
12use pyo3::exceptions::PyFloatingPointError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16use crate::neurons::FitzHughRinzelNeuron;
17
18type PyFitzHughRinzelBatch<'py> = (Bound<'py, PyArray1<f64>>, i64, f64, f64, f64);
19
20py_neuron_default!("FitzHughRinzelNeuron", PyFitzHughRinzelNeuron, FitzHughRinzelNeuron, state v, state w, state y);
21
22/// Register the FitzHugh-Rinzel class and batch simulator.
23pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
24    module.add_class::<PyFitzHughRinzelNeuron>()?;
25    module.add_function(wrap_pyfunction!(py_fitzhugh_rinzel_simulate, module)?)?;
26    Ok(())
27}
28
29/// Parity contract with
30/// `sc_neurocore.neurons.models.fitzhugh_rinzel.FitzHughRinzelNeuron.simulate`:
31/// for the same parameters and constant input the returned `v` trace, upward-
32/// crossing spike count, and final `(v, w, y)` state are bit-identical to the
33/// Python RK4 reference (the right-hand side is exact arithmetic — `v.powi(3)`
34/// = `v*v*v`, additions and multiplications, no transcendental functions).
35#[pyfunction]
36#[pyo3(signature = (v0, w0, y0, a, b, c, d, delta, mu, dt, v_threshold, n_steps, current))]
37#[allow(clippy::too_many_arguments)]
38fn py_fitzhugh_rinzel_simulate<'py>(
39    py: Python<'py>,
40    v0: f64,
41    w0: f64,
42    y0: f64,
43    a: f64,
44    b: f64,
45    c: f64,
46    d: f64,
47    delta: f64,
48    mu: f64,
49    dt: f64,
50    v_threshold: f64,
51    n_steps: usize,
52    current: f64,
53) -> PyResult<PyFitzHughRinzelBatch<'py>> {
54    let mut neuron = FitzHughRinzelNeuron {
55        v: v0,
56        w: w0,
57        y: y0,
58        a,
59        b,
60        c,
61        d,
62        delta,
63        mu,
64        dt,
65        v_threshold,
66    };
67    let Some((trace, spikes)) = neuron.try_simulate(n_steps, current) else {
68        return Err(PyFloatingPointError::new_err(
69            "FitzHugh-Rinzel Rust batch rejected an invalid candidate",
70        ));
71    };
72    Ok((trace.into_pyarray(py), spikes, neuron.v, neuron.w, neuron.y))
73}