ruvector_mincut/connectivity/
cache_opt.rs1use std::collections::{HashMap, HashSet, VecDeque};
16use crate::graph::VertexId;
17
18#[derive(Debug, Clone)]
22pub struct CacheOptAdjacency {
23 neighbors: Vec<(VertexId, f64)>,
25 offsets: Vec<usize>,
27 vertex_count: usize,
29}
30
31impl CacheOptAdjacency {
32 pub fn from_edges(edges: &[(VertexId, VertexId, f64)], max_vertex: VertexId) -> Self {
34 let vertex_count = (max_vertex + 1) as usize;
35 let mut adj: Vec<Vec<(VertexId, f64)>> = vec![Vec::new(); vertex_count];
36
37 for &(u, v, w) in edges {
38 adj[u as usize].push((v, w));
39 adj[v as usize].push((u, w));
40 }
41
42 let mut neighbors = Vec::with_capacity(edges.len() * 2);
44 let mut offsets = Vec::with_capacity(vertex_count + 1);
45 offsets.push(0);
46
47 for vertex_neighbors in &adj {
48 neighbors.extend_from_slice(vertex_neighbors);
49 offsets.push(neighbors.len());
50 }
51
52 Self {
53 neighbors,
54 offsets,
55 vertex_count,
56 }
57 }
58
59 #[inline]
61 pub fn neighbors(&self, v: VertexId) -> &[(VertexId, f64)] {
62 let v = v as usize;
63 if v >= self.vertex_count {
64 return &[];
65 }
66 &self.neighbors[self.offsets[v]..self.offsets[v + 1]]
67 }
68
69 #[inline]
75 pub fn prefetch_neighbors(&self, v: VertexId) {
76 let v = v as usize;
78 if v < self.vertex_count {
79 let _start = self.offsets[v];
80 }
84 }
85
86 pub fn vertex_count(&self) -> usize {
88 self.vertex_count
89 }
90}
91
92pub struct CacheOptBFS<'a> {
96 adj: &'a CacheOptAdjacency,
97 visited: Vec<bool>,
98 queue: VecDeque<VertexId>,
99 prefetch_distance: usize,
101}
102
103impl<'a> CacheOptBFS<'a> {
104 pub fn new(adj: &'a CacheOptAdjacency, start: VertexId) -> Self {
106 let mut visited = vec![false; adj.vertex_count()];
107 let mut queue = VecDeque::with_capacity(adj.vertex_count());
108
109 if (start as usize) < adj.vertex_count() {
110 visited[start as usize] = true;
111 queue.push_back(start);
112 }
113
114 Self {
115 adj,
116 visited,
117 queue,
118 prefetch_distance: 4,
119 }
120 }
121
122 pub fn run(mut self) -> HashSet<VertexId> {
124 let mut result = HashSet::new();
125
126 while let Some(v) = self.queue.pop_front() {
127 result.insert(v);
128
129 if let Some(&prefetch_v) = self.queue.get(self.prefetch_distance) {
131 self.adj.prefetch_neighbors(prefetch_v);
132 }
133
134 for &(neighbor, _) in self.adj.neighbors(v) {
135 let idx = neighbor as usize;
136 if idx < self.visited.len() && !self.visited[idx] {
137 self.visited[idx] = true;
138 self.queue.push_back(neighbor);
139 }
140 }
141 }
142
143 result
144 }
145
146 pub fn connected_to(mut self, target: VertexId) -> bool {
148 if (target as usize) >= self.adj.vertex_count() {
149 return false;
150 }
151
152 while let Some(v) = self.queue.pop_front() {
153 if v == target {
154 return true;
155 }
156
157 if let Some(&prefetch_v) = self.queue.get(self.prefetch_distance) {
159 self.adj.prefetch_neighbors(prefetch_v);
160 }
161
162 for &(neighbor, _) in self.adj.neighbors(v) {
163 let idx = neighbor as usize;
164 if idx < self.visited.len() && !self.visited[idx] {
165 self.visited[idx] = true;
166 self.queue.push_back(neighbor);
167 }
168 }
169 }
170
171 false
172 }
173}
174
175pub struct BatchProcessor {
180 batch_size: usize,
182}
183
184impl BatchProcessor {
185 pub fn new() -> Self {
187 Self { batch_size: 32 }
188 }
189
190 pub fn with_batch_size(batch_size: usize) -> Self {
192 Self { batch_size }
193 }
194
195 pub fn process_batched<F>(&self, vertices: &[VertexId], mut f: F)
197 where
198 F: FnMut(&[VertexId]),
199 {
200 for chunk in vertices.chunks(self.batch_size) {
201 f(chunk);
202 }
203 }
204
205 pub fn compute_degrees(
207 &self,
208 adj: &CacheOptAdjacency,
209 vertices: &[VertexId],
210 ) -> HashMap<VertexId, usize> {
211 let mut degrees = HashMap::with_capacity(vertices.len());
212
213 for chunk in vertices.chunks(self.batch_size) {
214 for &v in chunk {
216 adj.prefetch_neighbors(v);
217 }
218
219 for &v in chunk {
221 degrees.insert(v, adj.neighbors(v).len());
222 }
223 }
224
225 degrees
226 }
227}
228
229impl Default for BatchProcessor {
230 fn default() -> Self {
231 Self::new()
232 }
233}
234
235#[repr(C, align(64))]
237pub struct AlignedBuffer<T, const N: usize> {
238 data: [T; N],
239}
240
241impl<T: Default + Copy, const N: usize> AlignedBuffer<T, N> {
242 pub fn new() -> Self
244 where
245 T: Default + Copy,
246 {
247 Self {
248 data: [T::default(); N],
249 }
250 }
251
252 pub fn as_slice(&self) -> &[T] {
254 &self.data
255 }
256
257 pub fn as_mut_slice(&mut self) -> &mut [T] {
259 &mut self.data
260 }
261}
262
263impl<T: Default + Copy, const N: usize> Default for AlignedBuffer<T, N> {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272
273 #[test]
274 fn test_cache_opt_adjacency() {
275 let edges = vec![
276 (0, 1, 1.0),
277 (1, 2, 1.0),
278 (2, 3, 1.0),
279 ];
280
281 let adj = CacheOptAdjacency::from_edges(&edges, 3);
282
283 assert_eq!(adj.vertex_count(), 4);
284 assert_eq!(adj.neighbors(0).len(), 1);
285 assert_eq!(adj.neighbors(1).len(), 2);
286 assert_eq!(adj.neighbors(2).len(), 2);
287 assert_eq!(adj.neighbors(3).len(), 1);
288 }
289
290 #[test]
291 fn test_cache_opt_bfs() {
292 let edges = vec![
293 (0, 1, 1.0),
294 (1, 2, 1.0),
295 (2, 3, 1.0),
296 ];
297
298 let adj = CacheOptAdjacency::from_edges(&edges, 3);
299 let bfs = CacheOptBFS::new(&adj, 0);
300 let visited = bfs.run();
301
302 assert!(visited.contains(&0));
303 assert!(visited.contains(&1));
304 assert!(visited.contains(&2));
305 assert!(visited.contains(&3));
306 }
307
308 #[test]
309 fn test_bfs_connectivity() {
310 let edges = vec![
311 (0, 1, 1.0),
312 (2, 3, 1.0),
313 ];
314
315 let adj = CacheOptAdjacency::from_edges(&edges, 3);
316
317 assert!(CacheOptBFS::new(&adj, 0).connected_to(1));
318 assert!(!CacheOptBFS::new(&adj, 0).connected_to(2));
319 }
320
321 #[test]
322 fn test_batch_processor() {
323 let edges = vec![
324 (0, 1, 1.0),
325 (1, 2, 1.0),
326 (2, 3, 1.0),
327 ];
328
329 let adj = CacheOptAdjacency::from_edges(&edges, 3);
330 let processor = BatchProcessor::new();
331
332 let vertices: Vec<VertexId> = (0..4).collect();
333 let degrees = processor.compute_degrees(&adj, &vertices);
334
335 assert_eq!(degrees.get(&0), Some(&1));
336 assert_eq!(degrees.get(&1), Some(&2));
337 assert_eq!(degrees.get(&2), Some(&2));
338 assert_eq!(degrees.get(&3), Some(&1));
339 }
340
341 #[test]
342 fn test_aligned_buffer() {
343 let buffer: AlignedBuffer<u64, 8> = AlignedBuffer::new();
344
345 let ptr = buffer.as_slice().as_ptr();
347 assert_eq!(ptr as usize % 64, 0);
348
349 assert_eq!(buffer.as_slice().len(), 8);
350 }
351}