sc_neurocore_engine/bindings/
glif.rs1use numpy::{IntoPyArray, PyArray1};
12use pyo3::exceptions::PyFloatingPointError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16use crate::neurons::GLIFNeuron;
17
18type GLIFBatchOutput<'py> = (Bound<'py, PyArray1<f64>>, i64, f64, f64, f64, f64, f64, f64);
19
20py_neuron_default!(
21 "GLIFNeuron",
22 PyGLIFNeuron,
23 GLIFNeuron,
24 state v,
25 state theta_spike,
26 state i_asc1,
27 state i_asc2,
28 state theta_voltage,
29 state refractory_remaining
30);
31
32pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
34 module.add_class::<PyGLIFNeuron>()?;
35 module.add_function(wrap_pyfunction!(py_glif_simulate, module)?)?;
36 Ok(())
37}
38
39#[pyfunction]
41#[pyo3(signature = (
42 v0, theta_spike0, i_asc1_0, i_asc2_0, theta_voltage0, refractory_remaining0,
43 e_l, capacitance, resistance, theta_inf, b_spike, b_voltage, a_voltage,
44 k_asc1, k_asc2, f_v, delta_v, delta_theta_spike, f_asc1, f_asc2,
45 delta_i_asc1, delta_i_asc2, refractory_period, dt, n_steps, current
46))]
47#[allow(clippy::too_many_arguments)]
48fn py_glif_simulate<'py>(
49 py: Python<'py>,
50 v0: f64,
51 theta_spike0: f64,
52 i_asc1_0: f64,
53 i_asc2_0: f64,
54 theta_voltage0: f64,
55 refractory_remaining0: f64,
56 e_l: f64,
57 capacitance: f64,
58 resistance: f64,
59 theta_inf: f64,
60 b_spike: f64,
61 b_voltage: f64,
62 a_voltage: f64,
63 k_asc1: f64,
64 k_asc2: f64,
65 f_v: f64,
66 delta_v: f64,
67 delta_theta_spike: f64,
68 f_asc1: f64,
69 f_asc2: f64,
70 delta_i_asc1: f64,
71 delta_i_asc2: f64,
72 refractory_period: f64,
73 dt: f64,
74 n_steps: usize,
75 current: f64,
76) -> PyResult<GLIFBatchOutput<'py>> {
77 let mut neuron = GLIFNeuron {
78 v: v0,
79 theta_spike: theta_spike0,
80 i_asc1: i_asc1_0,
81 i_asc2: i_asc2_0,
82 theta_voltage: theta_voltage0,
83 refractory_remaining: refractory_remaining0,
84 e_l,
85 capacitance,
86 resistance,
87 theta_inf,
88 b_spike,
89 b_voltage,
90 a_voltage,
91 k_asc1,
92 k_asc2,
93 f_v,
94 delta_v,
95 delta_theta_spike,
96 f_asc1,
97 f_asc2,
98 delta_i_asc1,
99 delta_i_asc2,
100 refractory_period,
101 dt,
102 };
103 let Some((trace, events)) = neuron.try_simulate(n_steps, current) else {
104 return Err(PyFloatingPointError::new_err(
105 "GLIF5 Rust batch rejected an invalid candidate",
106 ));
107 };
108 Ok((
109 trace.into_pyarray(py),
110 events,
111 neuron.v,
112 neuron.theta_spike,
113 neuron.i_asc1,
114 neuron.i_asc2,
115 neuron.theta_voltage,
116 neuron.refractory_remaining,
117 ))
118}