sc_neurocore_engine/bindings/
poisson.rs1use numpy::{IntoPyArray, PyArray1};
12use pyo3::exceptions::PyValueError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16use crate::neurons;
17
18pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
20 module.add_class::<PyPoissonNeuron>()?;
21 module.add_function(wrap_pyfunction!(py_poisson_simulate, module)?)?;
22 Ok(())
23}
24
25#[pyclass(
26 name = "PoissonNeuron",
27 module = "sc_neurocore_engine.sc_neurocore_engine"
28)]
29#[derive(Clone)]
30pub struct PyPoissonNeuron {
31 inner: neurons::PoissonNeuron,
32}
33
34#[pymethods]
35impl PyPoissonNeuron {
36 #[new]
37 #[pyo3(signature = (rate_hz=100.0, dt_ms=1.0, seed=0xACE1))]
38 fn new(rate_hz: f64, dt_ms: f64, seed: u64) -> PyResult<Self> {
39 let inner = neurons::PoissonNeuron::new(rate_hz, dt_ms, seed);
40 if !inner.valid() {
41 return Err(PyValueError::new_err(
42 "invalid Poisson rate, timestep, or seed",
43 ));
44 }
45 Ok(Self { inner })
46 }
47 #[pyo3(signature = (rate_override=-1.0))]
48 fn step(&mut self, rate_override: f64) -> PyResult<i32> {
49 self.inner
50 .try_step(rate_override)
51 .map_err(PyValueError::new_err)
52 }
53 fn reset(&mut self) {
54 self.inner.reset();
55 }
56 fn get_state(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
57 let d = PyDict::new(py);
58 d.set_item("rate_hz", self.inner.rate_hz)?;
59 d.set_item("dt_ms", self.inner.dt_ms)?;
60 d.set_item("rng_state", self.inner.rng_state)?;
61 d.set_item("initial_seed", self.inner.initial_seed)?;
62 Ok(d.into_any().unbind())
63 }
64}
65
66#[pyfunction]
68#[pyo3(signature = (rate_hz, dt_ms, rng_state, n_steps, rate_override=-1.0))]
69fn py_poisson_simulate<'py>(
70 py: Python<'py>,
71 rate_hz: f64,
72 dt_ms: f64,
73 rng_state: u16,
74 n_steps: usize,
75 rate_override: f64,
76) -> PyResult<(Bound<'py, PyArray1<u8>>, u16)> {
77 let mut neuron = crate::neurons::PoissonNeuron {
78 rate_hz,
79 dt_ms,
80 rng_state,
81 initial_seed: rng_state,
82 };
83 if !neuron.valid() || !rate_override.is_finite() {
84 return Err(PyValueError::new_err(
85 "invalid Poisson simulation state or rate override",
86 ));
87 }
88 let mut events = Vec::with_capacity(n_steps);
89 for _ in 0..n_steps {
90 let spike = neuron
91 .try_step(rate_override)
92 .map_err(PyValueError::new_err)?;
93 events.push(spike as u8);
94 }
95 Ok((events.into_pyarray(py), neuron.rng_state))
96}