Skip to main content

sc_neurocore_engine/bindings/
ping.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 — PING circuit PyO3 binding
8
9//! Python binding for the Börgers-Kopell PING circuit step kernel.
10
11use numpy::{PyReadonlyArray1, PyReadwriteArray1};
12use pyo3::prelude::*;
13
14use crate::ping;
15
16/// Register the PING circuit step kernel with the extension module.
17pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
18    module.add_function(wrap_pyfunction!(py_ping_step, module)?)?;
19    Ok(())
20}
21
22/// Advance excitatory and inhibitory PING populations by one time step.
23///
24/// The caller supplies per-instance state arrays and pre-drawn noise samples
25/// so Python and Rust preserve the same seeded random sequence. State and
26/// spike arrays are updated in place; the return value contains the two
27/// population spike counts needed for the caller's conductance update.
28#[pyfunction]
29#[pyo3(signature = (
30    v_e, g_ampa_e, g_gaba_e, refrac_e, i_drive_e, xi_e, spikes_e_out,
31    v_i, g_ampa_i, g_gaba_i, refrac_i, i_drive_i, xi_i, spikes_i_out,
32    e_l, e_ampa, e_gaba, g_l, c_m, v_threshold, v_reset, t_refrac,
33    tau_ampa, tau_gaba, sigma_e, sigma_i, dt,
34))]
35#[allow(clippy::too_many_arguments)]
36fn py_ping_step<'py>(
37    _py: Python<'py>,
38    v_e: PyReadwriteArray1<'_, f64>,
39    g_ampa_e: PyReadwriteArray1<'_, f64>,
40    g_gaba_e: PyReadwriteArray1<'_, f64>,
41    refrac_e: PyReadwriteArray1<'_, f64>,
42    i_drive_e: PyReadonlyArray1<'_, f64>,
43    xi_e: PyReadonlyArray1<'_, f64>,
44    spikes_e_out: PyReadwriteArray1<'_, u8>,
45    v_i: PyReadwriteArray1<'_, f64>,
46    g_ampa_i: PyReadwriteArray1<'_, f64>,
47    g_gaba_i: PyReadwriteArray1<'_, f64>,
48    refrac_i: PyReadwriteArray1<'_, f64>,
49    i_drive_i: PyReadonlyArray1<'_, f64>,
50    xi_i: PyReadonlyArray1<'_, f64>,
51    spikes_i_out: PyReadwriteArray1<'_, u8>,
52    e_l: f64,
53    e_ampa: f64,
54    e_gaba: f64,
55    g_l: f64,
56    c_m: f64,
57    v_threshold: f64,
58    v_reset: f64,
59    t_refrac: f64,
60    tau_ampa: f64,
61    tau_gaba: f64,
62    sigma_e: f64,
63    sigma_i: f64,
64    dt: f64,
65) -> PyResult<(u32, u32)> {
66    let mut v_e = v_e;
67    let mut g_ampa_e = g_ampa_e;
68    let mut g_gaba_e = g_gaba_e;
69    let mut refrac_e = refrac_e;
70    let mut spikes_e_out = spikes_e_out;
71    let mut v_i = v_i;
72    let mut g_ampa_i = g_ampa_i;
73    let mut g_gaba_i = g_gaba_i;
74    let mut refrac_i = refrac_i;
75    let mut spikes_i_out = spikes_i_out;
76    let (ne, ni) = ping::step_kernel(
77        v_e.as_slice_mut()?,
78        g_ampa_e.as_slice_mut()?,
79        g_gaba_e.as_slice_mut()?,
80        refrac_e.as_slice_mut()?,
81        i_drive_e.as_slice()?,
82        xi_e.as_slice()?,
83        spikes_e_out.as_slice_mut()?,
84        v_i.as_slice_mut()?,
85        g_ampa_i.as_slice_mut()?,
86        g_gaba_i.as_slice_mut()?,
87        refrac_i.as_slice_mut()?,
88        i_drive_i.as_slice()?,
89        xi_i.as_slice()?,
90        spikes_i_out.as_slice_mut()?,
91        e_l,
92        e_ampa,
93        e_gaba,
94        g_l,
95        c_m,
96        v_threshold,
97        v_reset,
98        t_refrac,
99        tau_ampa,
100        tau_gaba,
101        sigma_e,
102        sigma_i,
103        dt,
104    );
105    Ok((ne, ni))
106}