Skip to main content

sc_neurocore_engine/bindings/
wilson_cowan.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-Cowan PyO3 binding
8
9//! Python binding for the Wilson-Cowan 1972 excitatory/inhibitory rate model.
10
11use crate::wilson_cowan;
12use numpy::{IntoPyArray, PyReadonlyArray1};
13use pyo3::exceptions::PyValueError;
14use pyo3::prelude::*;
15use pyo3::types::PyDict;
16
17/// Register the Wilson-Cowan batch simulator with the extension module.
18pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
19    module.add_function(wrap_pyfunction!(py_wilson_cowan_simulate, module)?)?;
20    Ok(())
21}
22
23/// Simulate a single Wilson-Cowan E/I unit for `ext_input.len()` steps
24/// and return per-step `e`, `i` traces plus final scalars.
25///
26/// Returns a dict with keys: `e`, `i` (1-D float64 arrays of length
27/// `n_steps`) + the final scalars `e_final`, `i_final`.
28#[pyfunction]
29#[pyo3(signature = (
30    e_init, i_init,
31    w_ee, w_ei, w_ie, w_ii,
32    tau_e, tau_i,
33    a, theta, dt,
34    ext_input,
35))]
36#[allow(clippy::too_many_arguments)]
37fn py_wilson_cowan_simulate<'py>(
38    py: Python<'py>,
39    e_init: f64,
40    i_init: f64,
41    w_ee: f64,
42    w_ei: f64,
43    w_ie: f64,
44    w_ii: f64,
45    tau_e: f64,
46    tau_i: f64,
47    a: f64,
48    theta: f64,
49    dt: f64,
50    ext_input: PyReadonlyArray1<'py, f64>,
51) -> PyResult<Py<PyAny>> {
52    let ext = ext_input.as_slice()?;
53    let n = ext.len();
54    let mut e_out = vec![0.0_f64; n];
55    let mut i_out = vec![0.0_f64; n];
56    let (e_final, i_final) = wilson_cowan::simulate(
57        e_init, i_init, w_ee, w_ei, w_ie, w_ii, tau_e, tau_i, a, theta, dt, ext, &mut e_out,
58        &mut i_out,
59    )
60    .map_err(PyValueError::new_err)?;
61    let d = PyDict::new(py);
62    d.set_item("e", e_out.into_pyarray(py))?;
63    d.set_item("i", i_out.into_pyarray(py))?;
64    d.set_item("e_final", e_final)?;
65    d.set_item("i_final", i_final)?;
66    Ok(d.into_any().unbind())
67}