1use core::cell::UnsafeCell;
6use std::sync::atomic::{AtomicU32, Ordering};
7use std::sync::OnceLock;
8
9use super::buffer_export::{parse_header, read_tensor_at};
10use super::q42_integration::{
11 Q42TensorView, TensorMetadata, TensorVolumeConfig, TensorVolumeError,
12};
13use super::Tensor10D;
14use crate::NQuin;
15
16pub const MAX_RESIDENT_NODES: usize = 4096;
18pub const MAX_KNN_HITS: usize = 32;
19
20pub struct ResidentTensorSubstrate {
22 nquins: UnsafeCell<[NQuin; MAX_RESIDENT_NODES]>,
23 metadata: UnsafeCell<[TensorMetadata; MAX_RESIDENT_NODES]>,
24 subject_hashes: UnsafeCell<[u64; MAX_RESIDENT_NODES]>,
25 node_count: AtomicU32,
26 load_generation: AtomicU32,
27}
28
29unsafe impl Sync for ResidentTensorSubstrate {}
31
32fn empty_nquin() -> NQuin {
33 NQuin {
34 subject: 0,
35 predicate: 0,
36 object: 0,
37 context: 0,
38 metadata: 0,
39 parity: 0,
40 }
41}
42
43impl ResidentTensorSubstrate {
44 pub fn new() -> Self {
45 Self {
46 nquins: UnsafeCell::new([empty_nquin(); MAX_RESIDENT_NODES]),
47 metadata: UnsafeCell::new([TensorMetadata::default(); MAX_RESIDENT_NODES]),
48 subject_hashes: UnsafeCell::new([0u64; MAX_RESIDENT_NODES]),
49 node_count: AtomicU32::new(0),
50 load_generation: AtomicU32::new(0),
51 }
52 }
53
54 #[inline]
55 pub fn node_count(&self) -> u32 {
56 self.node_count.load(Ordering::Acquire)
57 }
58
59 #[inline]
60 pub fn load_generation(&self) -> u32 {
61 self.load_generation.load(Ordering::Acquire)
62 }
63
64 #[inline]
65 pub fn subject_hash_at(&self, index: u32) -> u64 {
66 let count = self.node_count();
67 if index >= count {
68 return 0;
69 }
70 unsafe { (*self.subject_hashes.get())[index as usize] }
71 }
72
73 #[inline]
74 pub fn tensor_at(&self, index: u32) -> Option<Tensor10D> {
75 let count = self.node_count();
76 if index >= count {
77 return None;
78 }
79 let meta = unsafe { (*self.metadata.get())[index as usize] };
80 if meta.has_tensor {
81 Some(meta.tensor)
82 } else {
83 None
84 }
85 }
86
87 pub fn load_from_tensor_buffer(
89 &self,
90 bytes: &[u8],
91 default_subject_hash: u64,
92 ) -> Result<u32, &'static str> {
93 let (header, _header_len) = parse_header(bytes)?;
94 let count = header.node_count as usize;
95 if count > MAX_RESIDENT_NODES {
96 return Err("resident substrate capacity exceeded");
97 }
98
99 let nquins = unsafe { &mut *self.nquins.get() };
100 let metadata = unsafe { &mut *self.metadata.get() };
101 let subject_hashes = unsafe { &mut *self.subject_hashes.get() };
102
103 for i in 0..count {
104 let tensor = read_tensor_at(bytes, i)?;
105 let subject = if default_subject_hash != 0 {
106 default_subject_hash ^ (i as u64)
107 } else {
108 crate::q_hash(&format!("tensor:node:{i}"))
109 };
110 nquins[i] = stub_nquin_for_tensor(i, subject, &tensor);
111 metadata[i] = TensorMetadata::from_nquin_and_tensor(&nquins[i], tensor);
112 subject_hashes[i] = subject;
113 }
114
115 self.node_count.store(count as u32, Ordering::Release);
116 self.load_generation.fetch_add(1, Ordering::AcqRel);
117 Ok(count as u32)
118 }
119
120 pub fn tensor_search_into(
122 &self,
123 query: &Tensor10D,
124 max_distance: f32,
125 out: &mut [usize],
126 ) -> Result<usize, TensorVolumeError> {
127 let count = self.node_count() as usize;
128 if count == 0 {
129 return Ok(0);
130 }
131 let nquins =
132 unsafe { std::slice::from_raw_parts(self.nquins.get() as *const NQuin, count) };
133 let meta = unsafe {
134 std::slice::from_raw_parts(self.metadata.get() as *const TensorMetadata, count)
135 };
136 let config = TensorVolumeConfig::default();
137 let view = Q42TensorView::new(nquins, meta, &config)?;
138 view.tensor_search_into(query, max_distance, out)
139 }
140
141 pub fn load_from_tensors(
143 &self,
144 tensors: &[Tensor10D],
145 subject_hash: u64,
146 ) -> Result<u32, &'static str> {
147 if tensors.len() > MAX_RESIDENT_NODES {
148 return Err("resident substrate capacity exceeded");
149 }
150 let nquins = unsafe { &mut *self.nquins.get() };
151 let metadata = unsafe { &mut *self.metadata.get() };
152 let subject_hashes = unsafe { &mut *self.subject_hashes.get() };
153
154 for (i, tensor) in tensors.iter().enumerate() {
155 let subject = if subject_hash != 0 {
156 subject_hash ^ (i as u64)
157 } else {
158 crate::q_hash(&format!("tensor:node:{i}"))
159 };
160 nquins[i] = stub_nquin_for_tensor(i, subject, tensor);
161 metadata[i] = TensorMetadata::from_nquin_and_tensor(&nquins[i], *tensor);
162 subject_hashes[i] = subject;
163 }
164
165 self.node_count
166 .store(tensors.len() as u32, Ordering::Release);
167 self.load_generation.fetch_add(1, Ordering::AcqRel);
168 Ok(tensors.len() as u32)
169 }
170}
171
172#[inline]
173fn stub_nquin_for_tensor(index: usize, subject: u64, tensor: &Tensor10D) -> NQuin {
174 let object = ((tensor.x.to_bits() as u64) << 20)
175 ^ (tensor.y.to_bits() as u64)
176 ^ (tensor.z.to_bits() as u64);
177 NQuin {
178 subject,
179 predicate: 0,
180 object,
181 context: 0,
182 metadata: index as u64,
183 parity: subject ^ object,
184 }
185}
186
187static RESIDENT_SUBSTRATE: OnceLock<Box<ResidentTensorSubstrate>> = OnceLock::new();
188
189#[inline]
190pub fn global_resident_substrate() -> &'static ResidentTensorSubstrate {
191 RESIDENT_SUBSTRATE.get_or_init(|| Box::new(ResidentTensorSubstrate::new()))
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::tensor::buffer_export::{write_tensor_buffer, TensorBufferHeader};
198
199 #[test]
200 fn load_and_search_resident_substrate() {
201 let sub = ResidentTensorSubstrate::new();
202 let tensors = [
203 Tensor10D::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
204 Tensor10D::new(0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
205 ];
206 let need = TensorBufferHeader::total_bytes(2);
207 let mut buf = vec![0u8; need];
208 write_tensor_buffer(&tensors, &mut buf).unwrap();
209 assert_eq!(sub.load_from_tensor_buffer(&buf, 42).unwrap(), 2);
210
211 let query = Tensor10D::new(0.0, 0.0, 0.0, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
212 let mut hits = [0usize; MAX_KNN_HITS];
213 let n = sub.tensor_search_into(&query, 0.5, &mut hits).unwrap();
214 assert!(n >= 1);
215 assert_eq!(hits[0], 0);
216 }
217}