1#[cfg(not(target_arch = "wasm32"))]
6pub mod gpu_sieve {
7 use crate::NQuin;
8 use wgpu::util::DeviceExt;
9
10 #[repr(C)]
12 #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
13 pub struct FilterMask64 {
14 pub lo: u32,
15 pub hi: u32,
16 }
17
18 const BITMASK_WORDS: usize = 27;
20
21 pub struct SieveOrchestrator {
22 device: wgpu::Device,
23 queue: wgpu::Queue,
24 compute_pipeline: wgpu::ComputePipeline,
25 }
26
27 impl SieveOrchestrator {
28 pub async fn new() -> Option<Self> {
29 let instance = wgpu::Instance::default();
30 let adapter = instance
31 .request_adapter(&wgpu::RequestAdapterOptions {
32 power_preference: wgpu::PowerPreference::HighPerformance,
33 ..Default::default()
34 })
35 .await
36 .ok()?;
37 let (device, queue) = adapter
38 .request_device(&wgpu::DeviceDescriptor::default())
39 .await
40 .ok()?;
41
42 let shader_src = include_str!("../shaders/sieve.wgsl");
43 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
44 label: Some("Sieve Shader"),
45 source: wgpu::ShaderSource::Wgsl(shader_src.into()),
46 });
47
48 let compute_pipeline =
49 device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
50 label: Some("Sieve Pipeline"),
51 layout: None,
52 module: &shader,
53 entry_point: Some("main"),
54 compilation_options: Default::default(),
55 cache: None,
56 });
57
58 Some(Self {
59 device,
60 queue,
61 compute_pipeline,
62 })
63 }
64
65 pub async fn execute_sieve(&self, quins: &[NQuin], mask: u64) -> Option<Vec<u32>> {
69 let filter = FilterMask64 {
70 lo: mask as u32,
71 hi: (mask >> 32) as u32,
72 };
73
74 let quin_buffer = self
76 .device
77 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
78 label: Some("Quin Buffer"),
79 contents: bytemuck::cast_slice(quins),
80 usage: wgpu::BufferUsages::STORAGE,
81 });
82
83 let result_size = (BITMASK_WORDS * std::mem::size_of::<u32>()) as wgpu::BufferAddress;
85 let result_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
86 label: Some("Bitmask Buffer"),
87 size: result_size,
88 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
89 mapped_at_creation: false,
90 });
91
92 let filter_buffer = self
94 .device
95 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
96 label: Some("Filter Buffer"),
97 contents: bytemuck::cast_slice(&[filter]),
98 usage: wgpu::BufferUsages::UNIFORM,
99 });
100
101 let bind_group_layout = self.compute_pipeline.get_bind_group_layout(0);
102 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
103 label: Some("Sieve Bind Group"),
104 layout: &bind_group_layout,
105 entries: &[
106 wgpu::BindGroupEntry {
107 binding: 0,
108 resource: quin_buffer.as_entire_binding(),
109 },
110 wgpu::BindGroupEntry {
111 binding: 1,
112 resource: result_buffer.as_entire_binding(),
113 },
114 wgpu::BindGroupEntry {
115 binding: 2,
116 resource: filter_buffer.as_entire_binding(),
117 },
118 ],
119 });
120
121 let mut encoder = self
122 .device
123 .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
124 {
125 let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
126 label: None,
127 timestamp_writes: None,
128 });
129 cpass.set_pipeline(&self.compute_pipeline);
130 cpass.set_bind_group(0, &bind_group, &[]);
131
132 let workgroups = ((quins.len() as f32) / 64.0).ceil() as u32;
134 cpass.dispatch_workgroups(if workgroups == 0 { 1 } else { workgroups }, 1, 1);
135 }
136
137 let staging_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
139 label: Some("Staging Buffer"),
140 size: result_size,
141 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
142 mapped_at_creation: false,
143 });
144 encoder.copy_buffer_to_buffer(&result_buffer, 0, &staging_buffer, 0, result_size);
145
146 self.queue.submit(Some(encoder.finish()));
147
148 crate::telemetry::SIEVE_OPS_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
149
150 let buffer_slice = staging_buffer.slice(..);
152 let (sender, receiver) = futures_channel::oneshot::channel();
153 buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
154
155 let _ = self.device.poll(wgpu::PollType::wait_indefinitely());
156
157 if receiver.await.is_ok() {
158 let data = buffer_slice
159 .get_mapped_range()
160 .expect("wgpu buffer map_range failed");
161 let bitmask: &[u32] = bytemuck::cast_slice(&data);
162
163 let mut matching_indices: Vec<u32> = Vec::with_capacity(128);
165 for (bucket_idx, &bucket_val) in bitmask.iter().enumerate() {
166 let mut val = bucket_val;
167 while val != 0 {
168 let bit_shift = val.trailing_zeros();
169 matching_indices.push(bucket_idx as u32 * 32 + bit_shift);
170 val &= val - 1; }
172 }
173
174 drop(data);
175 staging_buffer.unmap();
176 return Some(matching_indices);
177 }
178
179 None
180 }
181 }
182}
183
184#[no_mangle]
188pub unsafe extern "C" fn nets_map_lorentz(
189 quins_ptr: *const crate::NQuin,
190 quins_len: usize,
191 out_lorentz_ptr: *mut crate::domains::mathematical::geometric::LorentzVector,
192) {
193 let quins = std::slice::from_raw_parts(quins_ptr, quins_len);
194 let out_lorentz = std::slice::from_raw_parts_mut(out_lorentz_ptr, quins_len);
195
196 for i in 0..quins_len {
197 out_lorentz[i] =
198 crate::domains::mathematical::geometric::LorentzVector::from_quin(&quins[i]);
199 }
200}
201
202#[no_mangle]
203pub unsafe extern "C" fn nets_tropical_voronoi_route(
204 queries_ptr: *const crate::domains::mathematical::geometric::LorentzVector,
205 queries_len: usize,
206 centroids_ptr: *const crate::domains::mathematical::geometric::MinPlusVoronoiCell,
207 centroids_len: usize,
208 out_cell_ids_ptr: *mut u32,
209) {
210 let queries = std::slice::from_raw_parts(queries_ptr, queries_len);
211 let centroids = std::slice::from_raw_parts(centroids_ptr, centroids_len);
212 let out_cell_ids = std::slice::from_raw_parts_mut(out_cell_ids_ptr, queries_len);
213
214 for i in 0..queries_len {
215 let query = &queries[i];
216 let mut best_id = 0;
217 let mut min_dist = f32::MAX;
218
219 for centroid in centroids {
220 let dist = centroid.tropical_distance(query);
221 if dist < min_dist {
222 min_dist = dist;
223 best_id = centroid.cell_id;
224 }
225 }
226
227 out_cell_ids[i] = best_id;
228 crate::telemetry::SIEVE_OPS_COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
229 }
230}
231
232pub fn nets_parse_smiles(smiles: &str) -> Option<crate::NQuin> {
235 if smiles.is_empty() {
237 return None;
238 }
239
240 let generated_quin = crate::NQuin {
242 subject: crate::q_hash(smiles),
243 predicate: crate::q_hash("IS_SMILES"),
244 object: 0,
245 context: 0,
246 metadata: 0b01 << 61,
247 parity: 0,
248 };
249
250 Some(generated_quin)
251}
252
253pub fn nets_calculate_valency(molecule_quin: &crate::NQuin) -> i32 {
254 if molecule_quin.predicate == crate::q_hash("IS_SMILES") {
257 4 } else {
259 -1
260 }
261}