dspk_synapse/streaming/kernels/
template_reduce.rs1use cubecl::prelude::*;
2use dspk_core::compute::element_position;
3use dspk_core::compute::LaunchGeometry;
4
5#[cube(launch)]
12pub fn reduce_channel_templates_kernel(
13 snippets: &[f32],
14 spike_order: &[u32],
15 segment_offsets: &[u32],
16 out_mean: &mut [f32],
17 out_m2: &mut [f32],
18 num_channels: u32,
19 elems_per_spike: u32,
20) {
21 let tid = element_position() as u32;
22 if tid < num_channels * elems_per_spike {
23 let ch = tid / elems_per_spike;
24 let rem = tid - ch * elems_per_spike;
25 let start = segment_offsets[ch as usize];
26 let end = segment_offsets[(ch + 1u32) as usize];
27
28 let mut mean = 0.0f32;
29 let mut m2 = 0.0f32;
30 let mut j = start;
31 while j < end {
32 let spike = spike_order[j as usize];
33 let val = snippets[(spike * elems_per_spike + rem) as usize];
34 let n = (j - start + 1u32) as f32;
35 let delta = val - mean;
36 mean += delta / n;
37 m2 += delta * (val - mean);
38 j += 1u32;
39 }
40
41 out_mean[tid as usize] = mean;
42 out_m2[tid as usize] = m2;
43 }
44}
45
46pub struct BatchTemplateStats {
49 pub counts: Vec<u32>,
50 pub mean: Vec<f32>,
51 pub m2: Vec<f32>,
52}
53
54pub fn execute_reduce_templates_in_vram(
58 client: &Client,
59 snippets_handle: &cubecl::server::Handle,
60 primary_channels: &[u32],
61 channels: usize,
62 k_neighbors: usize,
63 snippet_len: usize,
64) -> BatchTemplateStats {
65 let num_spikes = primary_channels.len();
66 let elems_per_spike = k_neighbors * snippet_len;
67 let total_slots = channels * elems_per_spike;
68
69 let mut counts = vec![0u32; channels];
71 for &ch in primary_channels {
72 if let Some(c) = counts.get_mut(ch as usize) {
73 *c += 1;
74 }
75 }
76 let mut offsets = vec![0u32; channels + 1];
77 for ch in 0..channels {
78 offsets[ch + 1] = offsets[ch] + counts[ch];
79 }
80 let mut cursor = offsets.clone();
81 let mut order = vec![0u32; offsets[channels] as usize];
82 for (i, &ch) in primary_channels.iter().enumerate() {
83 let ch = ch as usize;
84 if ch < channels {
85 order[cursor[ch] as usize] = i as u32;
86 cursor[ch] += 1;
87 }
88 }
89 if order.is_empty() || total_slots == 0 {
90 return BatchTemplateStats { counts, mean: vec![0.0; total_slots], m2: vec![0.0; total_slots] };
91 }
92
93 let order_handle = client.create_from_slice(u32::as_bytes(&order));
94 let offsets_handle = client.create_from_slice(u32::as_bytes(&offsets));
95 let out_mean_handle = client.empty(total_slots * std::mem::size_of::<f32>());
96 let out_m2_handle = client.empty(total_slots * std::mem::size_of::<f32>());
97
98 let geom = LaunchGeometry::elementwise(client, total_slots);
99 unsafe {
100 reduce_channel_templates_kernel::launch(
101 client,
102 geom.cube_count,
103 geom.cube_dim,
104 BufferArg::from_raw_parts(snippets_handle.clone(), num_spikes * elems_per_spike),
105 BufferArg::from_raw_parts(order_handle, order.len()),
106 BufferArg::from_raw_parts(offsets_handle, channels + 1),
107 BufferArg::from_raw_parts(out_mean_handle.clone(), total_slots),
108 BufferArg::from_raw_parts(out_m2_handle.clone(), total_slots),
109 channels as u32,
110 elems_per_spike as u32,
111 );
112 }
113
114 let mean_bytes = client.read_one(out_mean_handle).expect("VRAM read mean");
115 let m2_bytes = client.read_one(out_m2_handle).expect("VRAM read m2");
116 BatchTemplateStats {
117 counts,
118 mean: f32::from_bytes(&mean_bytes).to_vec(),
119 m2: f32::from_bytes(&m2_bytes).to_vec(),
120 }
121}