sc_neurocore_engine/bindings/
dense_layer.rs1use numpy::{
12 IntoPyArray, PyArray1, PyArray2, PyArrayMethods, PyReadonlyArray1, PyReadonlyArray2,
13 PyUntypedArrayMethods,
14};
15use pyo3::exceptions::PyValueError;
16use pyo3::prelude::*;
17
18use crate::layer;
19
20pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
22 module.add_class::<DenseLayer>()?;
23 Ok(())
24}
25
26#[pyclass(module = "sc_neurocore_engine.sc_neurocore_engine")]
27pub struct DenseLayer {
28 inner: layer::DenseLayer,
29}
30
31#[pymethods]
32impl DenseLayer {
33 #[new]
34 #[pyo3(signature = (n_inputs, n_neurons, length=1024, seed=24301))]
35 fn new(n_inputs: usize, n_neurons: usize, length: usize, seed: u64) -> Self {
36 Self {
37 inner: layer::DenseLayer::new(n_inputs, n_neurons, length, seed),
38 }
39 }
40
41 fn get_weights(&self) -> Vec<Vec<f64>> {
42 self.inner.get_weights()
43 }
44
45 fn set_weights(&mut self, weights: Vec<Vec<f64>>) -> PyResult<()> {
46 self.inner
47 .set_weights(weights)
48 .map_err(PyValueError::new_err)
49 }
50
51 fn refresh_packed_weights(&mut self) {
52 self.inner.refresh_packed_weights();
53 }
54
55 #[pyo3(signature = (input_values, seed=44257))]
56 fn forward(&self, input_values: Vec<f64>, seed: u64) -> PyResult<Vec<f64>> {
57 self.inner
58 .forward(&input_values, seed)
59 .map_err(PyValueError::new_err)
60 }
61
62 #[pyo3(signature = (input_values, seed=44257))]
63 fn forward_fast(&self, input_values: Vec<f64>, seed: u64) -> PyResult<Vec<f64>> {
64 self.inner
65 .forward_fast(&input_values, seed)
66 .map_err(PyValueError::new_err)
67 }
68
69 #[pyo3(signature = (input_values, seed=44257))]
73 fn forward_numpy<'py>(
74 &self,
75 py: Python<'py>,
76 input_values: PyReadonlyArray1<'py, f64>,
77 seed: u64,
78 ) -> PyResult<Bound<'py, PyArray1<f64>>> {
79 let slice = input_values
80 .as_slice()
81 .map_err(|e| PyValueError::new_err(format!("Cannot read input array: {e}")))?;
82 let out = self
83 .inner
84 .forward_numpy_inner(slice, seed)
85 .map_err(PyValueError::new_err)?;
86 Ok(out.into_pyarray(py))
87 }
88
89 #[pyo3(signature = (inputs, seed=44257))]
94 fn forward_batch_numpy<'py>(
95 &self,
96 py: Python<'py>,
97 inputs: PyReadonlyArray2<'py, f64>,
98 seed: u64,
99 ) -> PyResult<Bound<'py, PyArray2<f64>>> {
100 let shape = inputs.shape();
101 let n_samples = shape[0];
102 let n_inputs = shape[1];
103 if n_inputs != self.inner.n_inputs {
104 return Err(PyValueError::new_err(format!(
105 "Expected {} input features, got {}.",
106 self.inner.n_inputs, n_inputs
107 )));
108 }
109
110 let flat_inputs = inputs
111 .as_slice()
112 .map_err(|e| PyValueError::new_err(format!("Array not contiguous: {e}")))?;
113 let out = PyArray2::<f64>::zeros(py, [n_samples, self.inner.n_neurons], false);
114 let out_slice = unsafe {
116 out.as_slice_mut()
117 .expect("newly allocated output array must be contiguous")
118 };
119
120 self.inner
121 .forward_batch_into(flat_inputs, n_samples, seed, out_slice)
122 .map_err(PyValueError::new_err)?;
123 Ok(out)
124 }
125
126 fn forward_prepacked(&self, packed_inputs: &Bound<'_, PyAny>) -> PyResult<Vec<f64>> {
132 if let Ok(arr) = packed_inputs.extract::<PyReadonlyArray2<u64>>() {
133 let view = arr.as_array();
134 let rows: Vec<Vec<u64>> = (0..view.nrows()).map(|i| view.row(i).to_vec()).collect();
135 return self
136 .inner
137 .forward_prepacked(&rows)
138 .map_err(PyValueError::new_err);
139 }
140
141 let rows = packed_inputs.extract::<Vec<Vec<u64>>>().map_err(|_| {
142 PyValueError::new_err(
143 "packed_inputs must be a 2-D numpy uint64 array or list[list[int]].",
144 )
145 })?;
146 self.inner
147 .forward_prepacked(&rows)
148 .map_err(PyValueError::new_err)
149 }
150
151 #[pyo3(signature = (packed_inputs,))]
156 fn forward_prepacked_numpy<'py>(
157 &self,
158 py: Python<'py>,
159 packed_inputs: PyReadonlyArray2<'py, u64>,
160 ) -> PyResult<Bound<'py, PyArray1<f64>>> {
161 let shape = packed_inputs.shape();
162 let n_inputs = shape[0];
163 let words = shape[1];
164 let flat = packed_inputs
165 .as_slice()
166 .map_err(|e| PyValueError::new_err(format!("Array not contiguous: {e}")))?;
167 let out = self
168 .inner
169 .forward_prepacked_2d(flat, n_inputs, words)
170 .map_err(PyValueError::new_err)?;
171 Ok(out.into_pyarray(py))
172 }
173}