Skip to main content

sc_neurocore_engine/bindings/
hdc.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 — HDC BitStreamTensor PyO3 binding
8
9//! Python binding for the packed binary vector used by HDC/VSA operations.
10
11use pyo3::exceptions::PyValueError;
12use pyo3::prelude::*;
13use rand::SeedableRng;
14
15/// Register the HDC/VSA binding with the extension module.
16pub(crate) fn register(module: &Bound<'_, PyModule>) -> PyResult<()> {
17    module.add_class::<PyBitStreamTensor>()?;
18    Ok(())
19}
20
21/// Python wrapper for a packed binary hypervector.
22#[pyclass(
23    name = "BitStreamTensor",
24    module = "sc_neurocore_engine.sc_neurocore_engine"
25)]
26pub struct PyBitStreamTensor {
27    inner: crate::bitstream::BitStreamTensor,
28}
29
30#[pymethods]
31impl PyBitStreamTensor {
32    /// Create a random binary vector of `dimension` bits.
33    #[new]
34    #[pyo3(signature = (dimension=10000, seed=0xACE1))]
35    fn new(dimension: usize, seed: u64) -> Self {
36        let mut rng = rand_xoshiro::Xoshiro256PlusPlus::seed_from_u64(seed);
37        let data = crate::bitstream::bernoulli_packed(0.5, dimension, &mut rng);
38        Self {
39            inner: crate::bitstream::BitStreamTensor::from_words(data, dimension),
40        }
41    }
42
43    /// Create from pre-packed u64 words.
44    #[staticmethod]
45    fn from_packed(data: Vec<u64>, length: usize) -> PyResult<Self> {
46        if length == 0 {
47            return Err(PyValueError::new_err("bitstream length must be > 0"));
48        }
49        Ok(Self {
50            inner: crate::bitstream::BitStreamTensor::from_words(data, length),
51        })
52    }
53
54    /// In-place XOR (HDC bind).
55    fn xor_inplace(&mut self, other: &PyBitStreamTensor) {
56        self.inner.xor_inplace(&other.inner);
57    }
58
59    /// XOR returning a new tensor (HDC bind).
60    fn xor(&self, other: &PyBitStreamTensor) -> PyBitStreamTensor {
61        PyBitStreamTensor {
62            inner: self.inner.xor(&other.inner),
63        }
64    }
65
66    /// Cyclic right rotation by `shift` bits (HDC permute).
67    fn rotate_right(&mut self, shift: usize) {
68        self.inner.rotate_right(shift);
69    }
70
71    /// Normalized Hamming distance (0.0 = identical, 1.0 = opposite).
72    fn hamming_distance(&self, other: &PyBitStreamTensor) -> f32 {
73        self.inner.hamming_distance(&other.inner)
74    }
75
76    /// Majority-vote bundle of multiple tensors.
77    #[staticmethod]
78    fn bundle(vectors: Vec<PyRef<'_, PyBitStreamTensor>>) -> PyBitStreamTensor {
79        let refs: Vec<&crate::bitstream::BitStreamTensor> =
80            vectors.iter().map(|vector| &vector.inner).collect();
81        PyBitStreamTensor {
82            inner: crate::bitstream::BitStreamTensor::bundle(&refs),
83        }
84    }
85
86    /// Count of set bits.
87    fn popcount(&self) -> u64 {
88        crate::bitstream::popcount(&self.inner)
89    }
90
91    /// Packed u64 words (read-only copy).
92    #[getter]
93    fn data(&self) -> Vec<u64> {
94        self.inner.data.clone()
95    }
96
97    /// Logical bit length.
98    #[getter]
99    fn length(&self) -> usize {
100        self.inner.length
101    }
102
103    fn __len__(&self) -> usize {
104        self.inner.length
105    }
106
107    fn __repr__(&self) -> String {
108        format!(
109            "BitStreamTensor(length={}, popcount={})",
110            self.inner.length,
111            crate::bitstream::popcount(&self.inner)
112        )
113    }
114}