sc_neurocore_engine/bindings/
lapicque_neuron.rs1use numpy::{IntoPyArray, PyArray1};
10use pyo3::exceptions::{PyFloatingPointError, PyValueError};
11use pyo3::prelude::*;
12use pyo3::types::PyDict;
13
14use crate::neuron;
15
16#[pyclass(
17 name = "LapicqueNeuron",
18 module = "sc_neurocore_engine.sc_neurocore_engine"
19)]
20#[derive(Clone)]
21pub struct PyLapicqueNeuron {
22 inner: neuron::LapicqueNeuron,
23}
24
25#[pymethods]
26impl PyLapicqueNeuron {
27 #[new]
28 #[pyo3(signature = (tau=20.0, resistance=1.0, threshold=1.0, dt=1.0))]
29 fn new(tau: f64, resistance: f64, threshold: f64, dt: f64) -> PyResult<Self> {
30 let inner = neuron::LapicqueNeuron::new(tau, resistance, threshold, dt);
31 if !inner.valid() {
32 return Err(PyValueError::new_err(
33 "Lapicque SC compatibility parameters violate the finite positive-RC contract",
34 ));
35 }
36 Ok(Self { inner })
37 }
38 fn step(&mut self, current: f64) -> PyResult<i32> {
39 self.inner
40 .try_step(current)
41 .map_err(|error| PyFloatingPointError::new_err(error.to_string()))
42 }
43 fn reset(&mut self) {
44 self.inner.reset();
45 }
46 fn get_state(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
47 let d = PyDict::new(py);
48 d.set_item("v", self.inner.v)?;
49 Ok(d.into_any().unbind())
50 }
51}
52
53pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
55 module.add_class::<PyLapicqueNeuron>()?;
56 module.add_function(wrap_pyfunction!(lapicque_simulate_complete, module)?)?;
57 Ok(())
58}
59
60type LapicqueCompletePacket<'py> = (
61 Bound<'py, PyArray1<f64>>,
62 Bound<'py, PyArray1<u8>>,
63 f64,
64 bool,
65);
66
67#[pyfunction]
69#[pyo3(signature = (
70 v, v_rest, v_reset, v_threshold, tau, resistance, dt, capacitance,
71 series_resistance, polarization_resistance, excited, source_profile,
72 n_steps, drive
73))]
74#[allow(clippy::too_many_arguments)]
75fn lapicque_simulate_complete<'py>(
76 py: Python<'py>,
77 v: f64,
78 v_rest: f64,
79 v_reset: f64,
80 v_threshold: f64,
81 tau: f64,
82 resistance: f64,
83 dt: f64,
84 capacitance: f64,
85 series_resistance: f64,
86 polarization_resistance: f64,
87 excited: bool,
88 source_profile: bool,
89 n_steps: usize,
90 drive: f64,
91) -> PyResult<LapicqueCompletePacket<'py>> {
92 let model = neuron::LapicqueNeuron {
93 v,
94 v_rest,
95 v_reset,
96 v_threshold,
97 tau,
98 resistance,
99 dt,
100 capacitance,
101 series_resistance,
102 polarization_resistance,
103 excited,
104 source_profile,
105 };
106 let (voltage, events, final_v, final_excited) =
107 model.simulate_complete(n_steps, drive).map_err(|error| {
108 PyFloatingPointError::new_err(format!("Lapicque batch rejected: {error}"))
109 })?;
110 Ok((
111 voltage.into_pyarray(py),
112 events.into_pyarray(py),
113 final_v,
114 final_excited,
115 ))
116}