Skip to main content

sc_neurocore_engine/bindings/
brunel.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// SC-NeuroCore — Brunel-network PyO3 binding
8
9//! Python binding for the fixed-point Brunel network simulator.
10
11use numpy::{IntoPyArray, PyArray1, PyReadonlyArray1};
12use pyo3::exceptions::PyValueError;
13use pyo3::prelude::*;
14
15use crate::brunel;
16
17/// Register the fixed-point Brunel network with the extension module.
18pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
19    module.add_class::<PyBrunelNetwork>()?;
20    Ok(())
21}
22
23#[pyclass(
24    name = "BrunelNetwork",
25    module = "sc_neurocore_engine.sc_neurocore_engine"
26)]
27pub struct PyBrunelNetwork {
28    inner: brunel::BrunelNetwork,
29}
30
31#[pymethods]
32impl PyBrunelNetwork {
33    #[new]
34    #[pyo3(signature = (
35        n_neurons,
36        w_indptr,
37        w_indices,
38        w_data,
39        leak_k,
40        gain_k,
41        ext_lambda,
42        ext_weight_fp,
43        data_width=16,
44        fraction=8,
45        v_rest=0,
46        v_reset=0,
47        v_threshold=256,
48        refractory_period=2,
49        seed=42
50    ))]
51    #[allow(clippy::too_many_arguments)]
52    fn new(
53        n_neurons: usize,
54        w_indptr: PyReadonlyArray1<'_, i64>,
55        w_indices: PyReadonlyArray1<'_, i64>,
56        w_data: PyReadonlyArray1<'_, i16>,
57        leak_k: i16,
58        gain_k: i16,
59        ext_lambda: f64,
60        ext_weight_fp: i16,
61        data_width: u32,
62        fraction: u32,
63        v_rest: i16,
64        v_reset: i16,
65        v_threshold: i16,
66        refractory_period: i32,
67        seed: u64,
68    ) -> PyResult<Self> {
69        let indptr = w_indptr
70            .as_slice()
71            .map_err(|e| PyValueError::new_err(format!("Cannot read w_indptr: {e}")))?;
72        let indices = w_indices
73            .as_slice()
74            .map_err(|e| PyValueError::new_err(format!("Cannot read w_indices: {e}")))?;
75        let data = w_data
76            .as_slice()
77            .map_err(|e| PyValueError::new_err(format!("Cannot read w_data: {e}")))?;
78
79        let row_offsets: Vec<usize> = indptr.iter().map(|&v| v as usize).collect();
80        let col_indices: Vec<usize> = indices.iter().map(|&v| v as usize).collect();
81        let values: Vec<i16> = data.to_vec();
82
83        let inner = brunel::BrunelNetwork::new(
84            n_neurons,
85            row_offsets,
86            col_indices,
87            values,
88            data_width,
89            fraction,
90            v_rest,
91            v_reset,
92            v_threshold,
93            refractory_period,
94            leak_k,
95            gain_k,
96            ext_lambda,
97            ext_weight_fp,
98            seed,
99        )
100        .map_err(PyValueError::new_err)?;
101
102        Ok(Self { inner })
103    }
104
105    fn run<'py>(&mut self, py: Python<'py>, n_steps: usize) -> Bound<'py, PyArray1<u32>> {
106        let counts = self.inner.run(n_steps);
107        counts.into_pyarray(py)
108    }
109}