Skip to main content

sc_neurocore_engine/bindings/
wilson_hr.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 — Wilson-HR neuron PyO3 binding
8
9//! Python binding for the Wilson-HR polynomial cortical neuron.
10
11use numpy::{IntoPyArray, PyArray1};
12use pyo3::exceptions::PyFloatingPointError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16use crate::neurons::WilsonHRNeuron;
17
18type PyWilsonHRBatch<'py> = (Bound<'py, PyArray1<f64>>, i64, f64, f64);
19
20py_neuron_default!("WilsonHRNeuron", PyWilsonHRNeuron, WilsonHRNeuron, state v, state r);
21
22/// Register the Wilson-HR class and simulator with the extension module.
23pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
24    module.add_class::<PyWilsonHRNeuron>()?;
25    module.add_function(wrap_pyfunction!(py_wilson_hr_simulate, module)?)?;
26    Ok(())
27}
28
29/// N-step Wilson (1999) polynomial cortical-neuron simulation.
30///
31/// Parity contract with
32/// `sc_neurocore.neurons.models.wilson_hr.WilsonHRNeuron.simulate`: for the same
33/// parameters and constant input the returned continuous `v` trace, sampled
34/// upward-crossing count, and final `(v, r)` state are bit-identical to the
35/// Python RK4 reference.
36#[pyfunction]
37#[pyo3(signature = (v0, r0, capacitance, tau_r, v_peak, dt, n_steps, current))]
38#[allow(clippy::too_many_arguments)]
39fn py_wilson_hr_simulate<'py>(
40    py: Python<'py>,
41    v0: f64,
42    r0: f64,
43    capacitance: f64,
44    tau_r: f64,
45    v_peak: f64,
46    dt: f64,
47    n_steps: usize,
48    current: f64,
49) -> PyResult<PyWilsonHRBatch<'py>> {
50    let mut neuron = WilsonHRNeuron {
51        v: v0,
52        r: r0,
53        capacitance,
54        tau_r,
55        v_peak,
56        dt,
57    };
58    let Some((trace, spikes)) = neuron.try_simulate(n_steps, current) else {
59        return Err(PyFloatingPointError::new_err(
60            "Wilson-HR Rust batch rejected an invalid candidate",
61        ));
62    };
63    Ok((trace.into_pyarray(py), spikes, neuron.v, neuron.r))
64}