Skip to main content

sc_neurocore_engine/bindings/trivial/
sigma_delta.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
8//! PyO3 exposure for the sampled APSDM contract.
9
10use crate::neurons;
11use numpy::{IntoPyArray, PyReadonlyArray1};
12use pyo3::exceptions::PyValueError;
13use pyo3::prelude::*;
14use pyo3::types::PyDict;
15
16#[pyclass(
17    name = "SigmaDeltaNeuron",
18    module = "sc_neurocore_engine.sc_neurocore_engine"
19)]
20#[derive(Clone)]
21pub struct PySigmaDeltaNeuron {
22    inner: neurons::SigmaDeltaNeuron,
23}
24
25#[pymethods]
26impl PySigmaDeltaNeuron {
27    #[new]
28    #[pyo3(signature=(sigma=0.0,reconstruction=0.0,delta=1.0,tau_reconstruction=10.0,dt=0.1))]
29    fn new(
30        sigma: f64,
31        reconstruction: f64,
32        delta: f64,
33        tau_reconstruction: f64,
34        dt: f64,
35    ) -> PyResult<Self> {
36        let inner = neurons::SigmaDeltaNeuron {
37            sigma,
38            reconstruction,
39            delta,
40            tau_reconstruction,
41            dt,
42        };
43        if !inner.validate() {
44            return Err(PyValueError::new_err(
45                "invalid SigmaDelta state or configuration",
46            ));
47        }
48        Ok(Self { inner })
49    }
50    fn step(&mut self, current: f64) -> PyResult<i32> {
51        self.inner.try_step(current).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("sigma", self.inner.sigma)?;
59        d.set_item("reconstruction", self.inner.reconstruction)?;
60        Ok(d.into_any().unbind())
61    }
62}
63
64#[pyfunction]
65#[pyo3(signature=(sigma,reconstruction,delta,tau_reconstruction,dt,currents))]
66fn py_sigma_delta_simulate<'py>(
67    py: Python<'py>,
68    sigma: f64,
69    reconstruction: f64,
70    delta: f64,
71    tau_reconstruction: f64,
72    dt: f64,
73    currents: PyReadonlyArray1<'py, f64>,
74) -> PyResult<Py<PyAny>> {
75    let mut n = neurons::SigmaDeltaNeuron {
76        sigma,
77        reconstruction,
78        delta,
79        tau_reconstruction,
80        dt,
81    };
82    if !n.validate() {
83        return Err(PyValueError::new_err(
84            "invalid SigmaDelta state or configuration",
85        ));
86    }
87    let mut sigmas = Vec::with_capacity(currents.len()?);
88    let mut reconstructions = Vec::with_capacity(currents.len()?);
89    let mut events = Vec::with_capacity(currents.len()?);
90    for &current in currents.as_slice()? {
91        events.push(n.try_step(current).map_err(PyValueError::new_err)?);
92        sigmas.push(n.sigma);
93        reconstructions.push(n.reconstruction);
94    }
95    let d = PyDict::new(py);
96    d.set_item("sigma", sigmas.into_pyarray(py))?;
97    d.set_item("reconstruction", reconstructions.into_pyarray(py))?;
98    d.set_item("events", events.into_pyarray(py))?;
99    d.set_item("sigma_final", n.sigma)?;
100    d.set_item("reconstruction_final", n.reconstruction)?;
101    Ok(d.into_any().unbind())
102}
103pub(super) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
104    module.add_class::<PySigmaDeltaNeuron>()?;
105    module.add_function(wrap_pyfunction!(py_sigma_delta_simulate, module)?)?;
106    Ok(())
107}