sc_neurocore_engine/bindings/trivial/
energy_lif.rs1use crate::neurons;
11use numpy::{IntoPyArray, PyReadonlyArray1};
12use pyo3::exceptions::PyValueError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16#[pyclass(
17 name = "EnergyLIFNeuron",
18 module = "sc_neurocore_engine.sc_neurocore_engine"
19)]
20#[derive(Clone)]
21pub struct PyEnergyLIFNeuron {
22 inner: neurons::EnergyLIFNeuron,
23}
24
25#[pymethods]
26impl PyEnergyLIFNeuron {
27 #[new]
28 #[pyo3(signature=(v=-61.0,epsilon=0.32,capacitance=100.0,g_leak=9.0,e_0=-62.5,e_u=-58.5,e_d=-40.0,e_f=-62.0,v_threshold=-59.0,v_reset=-62.0,alpha=1.0,epsilon_0=0.5,epsilon_c=0.18,delta=0.01,tau_e=200.0,dt=0.1))]
29 #[allow(clippy::too_many_arguments)]
30 fn new(
31 v: f64,
32 epsilon: f64,
33 capacitance: f64,
34 g_leak: f64,
35 e_0: f64,
36 e_u: f64,
37 e_d: f64,
38 e_f: f64,
39 v_threshold: f64,
40 v_reset: f64,
41 alpha: f64,
42 epsilon_0: f64,
43 epsilon_c: f64,
44 delta: f64,
45 tau_e: f64,
46 dt: f64,
47 ) -> PyResult<Self> {
48 let inner = neurons::EnergyLIFNeuron {
49 v,
50 epsilon,
51 capacitance,
52 g_leak,
53 e_0,
54 e_u,
55 e_d,
56 e_f,
57 v_threshold,
58 v_reset,
59 alpha,
60 epsilon_0,
61 epsilon_c,
62 delta,
63 tau_e,
64 dt,
65 };
66 if !inner.valid() {
67 return Err(PyValueError::new_err(
68 "invalid EnergyLIF state or configuration",
69 ));
70 }
71 Ok(Self { inner })
72 }
73 fn step(&mut self, current: f64) -> PyResult<i32> {
74 self.inner.try_step(current).map_err(PyValueError::new_err)
75 }
76 fn reset(&mut self) {
77 self.inner.reset();
78 }
79 fn get_state(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
80 let d = PyDict::new(py);
81 d.set_item("v", self.inner.v)?;
82 d.set_item("epsilon", self.inner.epsilon)?;
83 Ok(d.into_any().unbind())
84 }
85}
86
87#[pyfunction]
88#[pyo3(signature=(v,epsilon,capacitance,g_leak,e_0,e_u,e_d,e_f,v_threshold,v_reset,alpha,epsilon_0,epsilon_c,delta,tau_e,dt,currents))]
89#[allow(clippy::too_many_arguments)]
90fn py_energy_lif_simulate<'py>(
91 py: Python<'py>,
92 v: f64,
93 epsilon: f64,
94 capacitance: f64,
95 g_leak: f64,
96 e_0: f64,
97 e_u: f64,
98 e_d: f64,
99 e_f: f64,
100 v_threshold: f64,
101 v_reset: f64,
102 alpha: f64,
103 epsilon_0: f64,
104 epsilon_c: f64,
105 delta: f64,
106 tau_e: f64,
107 dt: f64,
108 currents: PyReadonlyArray1<'py, f64>,
109) -> PyResult<Py<PyAny>> {
110 let mut n = neurons::EnergyLIFNeuron {
111 v,
112 epsilon,
113 capacitance,
114 g_leak,
115 e_0,
116 e_u,
117 e_d,
118 e_f,
119 v_threshold,
120 v_reset,
121 alpha,
122 epsilon_0,
123 epsilon_c,
124 delta,
125 tau_e,
126 dt,
127 };
128 if !n.valid() {
129 return Err(PyValueError::new_err(
130 "invalid EnergyLIF state or configuration",
131 ));
132 }
133 let mut voltages = Vec::with_capacity(currents.len()?);
134 let mut energies = Vec::with_capacity(currents.len()?);
135 let mut events = Vec::with_capacity(currents.len()?);
136 for ¤t in currents.as_slice()? {
137 events.push(n.try_step(current).map_err(PyValueError::new_err)?);
138 voltages.push(n.v);
139 energies.push(n.epsilon);
140 }
141 let d = PyDict::new(py);
142 d.set_item("voltages", voltages.into_pyarray(py))?;
143 d.set_item("epsilon", energies.into_pyarray(py))?;
144 d.set_item("events", events.into_pyarray(py))?;
145 d.set_item("v_final", n.v)?;
146 d.set_item("epsilon_final", n.epsilon)?;
147 Ok(d.into_any().unbind())
148}
149
150pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
151 module.add_class::<PyEnergyLIFNeuron>()?;
152 module.add_function(wrap_pyfunction!(py_energy_lif_simulate, module)?)?;
153 Ok(())
154}