1use super::addr::VertexAddr;
2use super::vertex::VertexId;
3
4#[cfg(test)]
5mod tests {
6 use super::*;
7
8 use super::super::addr::GridAddr;
9
10 fn grid(row: u32, col: u32) -> VertexAddr {
11 VertexAddr::grid(GridAddr::new(row, col))
12 }
13
14 #[test]
15 fn test_csr_construction() {
16 let edges = vec![
17 (0u32, vec![1u32, 2u32]),
18 (1u32, vec![2u32, 3u32]),
19 (2u32, vec![3u32]),
20 (3u32, vec![]),
21 ];
22
23 let coords = vec![grid(0, 0), grid(0, 1), grid(1, 0), grid(1, 1)];
24
25 let csr = CsrEdges::from_adjacency(edges, &coords);
26
27 assert_eq!(csr.out_edges(VertexId(0)), &[VertexId(1), VertexId(2)]);
28 assert_eq!(csr.out_edges(VertexId(1)), &[VertexId(2), VertexId(3)]);
29 assert_eq!(csr.out_edges(VertexId(3)), &[]);
30 }
31
32 #[test]
33 fn test_csr_memory_efficiency() {
34 let mut edges = Vec::new();
36 let mut coords = Vec::new();
37
38 for i in 0..10_000u32 {
39 let targets: Vec<_> = (0..4).map(|j| (i + j + 1) % 10_000).collect();
40 edges.push((i, targets));
41 coords.push(grid(i, i));
42 }
43
44 let csr = CsrEdges::from_adjacency(edges, &coords);
45
46 assert!(csr.memory_usage() < 410_000, "{}", csr.memory_usage());
48 }
49
50 #[test]
51 fn test_csr_edge_ordering() {
52 let edges = vec![
54 (0u32, vec![3u32, 1u32, 2u32]), ];
56
57 let coords = vec![
58 grid(0, 0), grid(0, 5), grid(0, 3), grid(1, 0), ];
63
64 let csr = CsrEdges::from_adjacency(edges, &coords);
65
66 assert_eq!(
69 csr.out_edges(VertexId(0)),
70 &[VertexId(2), VertexId(1), VertexId(3)]
71 );
72 }
73
74 #[test]
75 fn test_csr_empty_graph() {
76 let edges: Vec<(u32, Vec<u32>)> = vec![];
77 let coords: Vec<VertexAddr> = vec![];
78
79 let csr = CsrEdges::from_adjacency(edges, &coords);
80
81 assert_eq!(csr.num_vertices(), 0);
82 assert_eq!(csr.num_edges(), 0);
83 assert_eq!(csr.memory_usage(), 8);
85 }
86
87 #[test]
88 fn test_csr_single_vertex() {
89 let edges = vec![(0u32, vec![])];
90 let coords = vec![grid(0, 0)];
91
92 let csr = CsrEdges::from_adjacency(edges, &coords);
93
94 assert_eq!(csr.num_vertices(), 1);
95 assert_eq!(csr.num_edges(), 0);
96 assert_eq!(csr.out_edges(VertexId(0)), &[]);
97 }
98
99 #[test]
100 fn test_csr_self_loop() {
101 let edges = vec![(0u32, vec![0u32])]; let coords = vec![grid(0, 0)];
103
104 let csr = CsrEdges::from_adjacency(edges, &coords);
105
106 assert_eq!(csr.out_edges(VertexId(0)), &[VertexId(0)]);
107 assert_eq!(csr.num_edges(), 1);
108 }
109
110 #[test]
111 fn test_csr_duplicate_edges() {
112 let edges = vec![(0u32, vec![1u32, 1u32, 2u32, 1u32])];
114 let coords = vec![grid(0, 0), grid(0, 1), grid(0, 2)];
115
116 let csr = CsrEdges::from_adjacency(edges, &coords);
117
118 assert_eq!(
120 csr.out_edges(VertexId(0)),
121 &[VertexId(1), VertexId(1), VertexId(1), VertexId(2)]
122 );
123 }
124
125 #[test]
126 fn test_degree_calculation() {
127 let edges = vec![
128 (0u32, vec![1u32, 2u32, 3u32]),
129 (1u32, vec![2u32]),
130 (2u32, vec![]),
131 (3u32, vec![0u32, 1u32]),
132 ];
133
134 let coords = vec![grid(0, 0), grid(0, 1), grid(1, 0), grid(1, 1)];
135
136 let csr = CsrEdges::from_adjacency(edges, &coords);
137
138 assert_eq!(csr.out_degree(VertexId(0)), 3);
139 assert_eq!(csr.out_degree(VertexId(1)), 1);
140 assert_eq!(csr.out_degree(VertexId(2)), 0);
141 assert_eq!(csr.out_degree(VertexId(3)), 2);
142 }
143
144 #[test]
145 fn test_out_of_bounds_access() {
146 let edges = vec![(0u32, vec![])];
147 let coords = vec![grid(0, 0)];
148
149 let csr = CsrEdges::from_adjacency(edges, &coords);
150
151 assert_eq!(csr.out_edges(VertexId(1)), &[]);
153 }
154
155 #[test]
156 fn test_csr_iterator() {
157 let edges = vec![
158 (0u32, vec![1u32, 2u32]),
159 (1u32, vec![3u32]),
160 (2u32, vec![1u32, 3u32]),
161 (3u32, vec![]),
162 ];
163
164 let coords = vec![grid(0, 0), grid(0, 1), grid(1, 0), grid(1, 1)];
165
166 let csr = CsrEdges::from_adjacency(edges, &coords);
167
168 let collected: Vec<_> = csr.iter().collect();
169 assert_eq!(collected.len(), 4);
170 assert_eq!(collected[0].0, VertexId(0));
171 assert_eq!(collected[0].1, &[VertexId(1), VertexId(2)]);
172 assert_eq!(collected[3].1, &[]);
173 }
174
175 #[test]
176 fn test_has_edge() {
177 let edges = vec![
178 (0u32, vec![1u32, 2u32]),
179 (1u32, vec![3u32]),
180 (2u32, vec![]),
181 (3u32, vec![0u32]), ];
183
184 let coords = vec![grid(0, 0), grid(0, 1), grid(1, 0), grid(1, 1)];
185
186 let csr = CsrEdges::from_adjacency(edges, &coords);
187
188 assert!(csr.has_edge(VertexId(0), VertexId(1)));
189 assert!(csr.has_edge(VertexId(0), VertexId(2)));
190 assert!(!csr.has_edge(VertexId(0), VertexId(3)));
191 assert!(csr.has_edge(VertexId(3), VertexId(0))); assert!(!csr.has_edge(VertexId(2), VertexId(0))); }
194
195 #[test]
196 fn test_csr_with_offset_vertex_ids() {
197 let base_id = 1024u32;
199 let edges = vec![
200 (base_id, vec![base_id + 1, base_id + 2]),
201 (base_id + 1, vec![base_id + 3]),
202 (base_id + 2, vec![base_id + 3]),
203 (base_id + 3, vec![]),
204 ];
205
206 let coords = vec![grid(0, 0), grid(0, 1), grid(1, 0), grid(1, 1)];
207
208 let csr = CsrEdges::from_adjacency(edges, &coords);
209
210 assert_eq!(csr.min_vertex_id, base_id);
212
213 assert_eq!(
215 csr.out_edges(VertexId(base_id)),
216 &[VertexId(base_id + 1), VertexId(base_id + 2)]
217 );
218 assert_eq!(
219 csr.out_edges(VertexId(base_id + 1)),
220 &[VertexId(base_id + 3)]
221 );
222 assert_eq!(
223 csr.out_edges(VertexId(base_id + 2)),
224 &[VertexId(base_id + 3)]
225 );
226 assert_eq!(csr.out_edges(VertexId(base_id + 3)), &[]);
227
228 assert_eq!(csr.out_edges(VertexId(0)), &[]); assert_eq!(csr.out_edges(VertexId(base_id + 100)), &[]); }
232
233 #[test]
234 fn test_csr_with_sparse_vertex_ids() {
235 let edges = vec![
237 (100u32, vec![300u32, 500u32]),
238 (300u32, vec![500u32]),
239 (500u32, vec![100u32]), ];
241
242 let coords = vec![
243 grid(0, 0), grid(0, 0), grid(1, 0), grid(0, 0), grid(2, 0), ];
249
250 let csr = CsrEdges::from_adjacency(edges, &coords);
251
252 assert_eq!(csr.min_vertex_id, 100);
254
255 assert_eq!(
257 csr.out_edges(VertexId(100)),
258 &[VertexId(300), VertexId(500)]
259 );
260 assert_eq!(csr.out_edges(VertexId(300)), &[VertexId(500)]);
261 assert_eq!(csr.out_edges(VertexId(500)), &[VertexId(100)]);
262
263 assert_eq!(csr.out_edges(VertexId(200)), &[]);
265 assert_eq!(csr.out_edges(VertexId(400)), &[]);
266 }
267}
268
269#[derive(Debug, Clone)]
277pub struct CsrEdges {
278 offsets: Vec<u32>,
282
283 edges: Vec<VertexId>,
285
286 reverse_offsets: Vec<u32>,
288
289 reverse_edges: Vec<VertexId>,
291
292 min_vertex_id: u32,
294}
295
296impl CsrEdges {
297 pub fn from_adjacency(adj: Vec<(u32, Vec<u32>)>, coords: &[VertexAddr]) -> Self {
307 if adj.is_empty() {
308 return Self {
309 offsets: vec![0],
310 edges: Vec::new(),
311 reverse_offsets: vec![0],
312 reverse_edges: Vec::new(),
313 min_vertex_id: 0,
314 };
315 }
316
317 let mut min_id = u32::MAX;
319 let mut max_id = 0;
320 for &(vid, ref targets) in &adj {
321 min_id = min_id.min(vid);
322 max_id = max_id.max(vid);
323 for &target in targets {
324 min_id = min_id.min(target);
325 max_id = max_id.max(target);
326 }
327 }
328
329 if min_id == u32::MAX {
331 return Self {
332 offsets: vec![0],
333 edges: Vec::new(),
334 reverse_offsets: vec![0],
335 reverse_edges: Vec::new(),
336 min_vertex_id: 0,
337 };
338 }
339
340 let num_vertices = (max_id - min_id + 1) as usize;
341 let mut offsets = vec![0u32; num_vertices + 1];
342 let mut edges = Vec::new();
343
344 let mut adj_by_offset: Vec<Vec<u32>> = vec![Vec::new(); num_vertices];
346 for (vid, targets) in adj {
347 let offset_idx = (vid - min_id) as usize;
348 adj_by_offset[offset_idx] = targets;
349 }
350
351 for (idx, mut targets) in adj_by_offset.clone().into_iter().enumerate() {
353 targets.sort_by_key(|&t| {
355 let coord_idx = (t - min_id) as usize;
357 if coord_idx < coords.len() {
358 let (major, minor) = coords[coord_idx].order_key();
359 (major, minor, t)
360 } else {
361 (u32::MAX, u32::MAX, t)
363 }
364 });
365
366 edges.extend(targets.into_iter().map(VertexId));
367 offsets[idx + 1] = edges.len() as u32;
368 }
369
370 let mut reverse_offsets = vec![0u32; num_vertices + 1];
372 let mut reverse_edges = Vec::new();
373 let mut reverse_adj: Vec<Vec<u32>> = vec![Vec::new(); num_vertices];
374
375 for (idx, targets) in adj_by_offset.into_iter().enumerate() {
377 let source = min_id + idx as u32;
378 for target in targets {
379 let target_idx = (target - min_id) as usize;
380 if target_idx < num_vertices {
381 reverse_adj[target_idx].push(source);
382 }
383 }
384 }
385
386 for (idx, mut sources) in reverse_adj.into_iter().enumerate() {
388 sources.sort_by_key(|&s| {
390 let coord_idx = (s - min_id) as usize;
391 if coord_idx < coords.len() {
392 let (major, minor) = coords[coord_idx].order_key();
393 (major, minor, s)
394 } else {
395 (u32::MAX, u32::MAX, s)
396 }
397 });
398
399 reverse_edges.extend(sources.into_iter().map(VertexId));
400 reverse_offsets[idx + 1] = reverse_edges.len() as u32;
401 }
402
403 Self {
404 offsets,
405 edges,
406 reverse_offsets,
407 reverse_edges,
408 min_vertex_id: min_id,
409 }
410 }
411
412 #[inline]
414 pub fn out_edges(&self, v: VertexId) -> &[VertexId] {
415 if self.offsets.len() <= 1 {
417 return &[];
418 }
419
420 if v.0 < self.min_vertex_id {
422 return &[];
423 }
424
425 let idx = (v.0 - self.min_vertex_id) as usize;
426 if idx >= self.offsets.len() - 1 {
427 return &[];
428 }
429
430 let start = self.offsets[idx] as usize;
431 let end = self.offsets[idx + 1] as usize;
432 &self.edges[start..end]
433 }
434
435 #[inline]
437 pub fn in_edges(&self, v: VertexId) -> &[VertexId] {
438 if self.reverse_offsets.len() <= 1 {
440 return &[];
441 }
442
443 if v.0 < self.min_vertex_id {
445 return &[];
446 }
447
448 let idx = (v.0 - self.min_vertex_id) as usize;
449 if idx >= self.reverse_offsets.len() - 1 {
450 return &[];
451 }
452
453 let start = self.reverse_offsets[idx] as usize;
454 let end = self.reverse_offsets[idx + 1] as usize;
455 &self.reverse_edges[start..end]
456 }
457
458 #[inline]
460 pub fn out_degree(&self, v: VertexId) -> usize {
461 if self.offsets.len() <= 1 {
463 return 0;
464 }
465
466 if v.0 < self.min_vertex_id {
468 return 0;
469 }
470
471 let idx = (v.0 - self.min_vertex_id) as usize;
472 if idx >= self.offsets.len() - 1 {
473 return 0;
474 }
475
476 let start = self.offsets[idx];
477 let end = self.offsets[idx + 1];
478 (end - start) as usize
479 }
480
481 #[inline]
483 pub fn in_degree(&self, v: VertexId) -> usize {
484 self.in_edges(v).len()
485 }
486
487 #[inline]
489 pub fn num_vertices(&self) -> usize {
490 self.offsets.len().saturating_sub(1)
491 }
492
493 #[inline]
495 pub fn num_edges(&self) -> usize {
496 self.edges.len()
497 }
498
499 pub fn memory_usage(&self) -> usize {
501 self.offsets.len() * std::mem::size_of::<u32>()
502 + self.edges.len() * std::mem::size_of::<VertexId>()
503 + self.reverse_offsets.len() * std::mem::size_of::<u32>()
504 + self.reverse_edges.len() * std::mem::size_of::<VertexId>()
505 }
506
507 pub fn empty() -> Self {
509 Self {
510 offsets: vec![0],
511 edges: Vec::new(),
512 reverse_offsets: vec![0],
513 reverse_edges: Vec::new(),
514 min_vertex_id: 0,
515 }
516 }
517
518 pub fn builder() -> CsrBuilder {
520 CsrBuilder::new()
521 }
522
523 pub fn iter(&'_ self) -> CsrIterator<'_> {
525 CsrIterator {
526 csr: self,
527 current_vertex: 0,
528 }
529 }
530
531 pub fn has_edge(&self, from: VertexId, to: VertexId) -> bool {
533 self.out_edges(from).contains(&to)
534 }
535}
536
537pub struct CsrIterator<'a> {
539 csr: &'a CsrEdges,
540 current_vertex: usize,
541}
542
543impl<'a> Iterator for CsrIterator<'a> {
544 type Item = (VertexId, &'a [VertexId]);
545
546 fn next(&mut self) -> Option<Self::Item> {
547 if self.current_vertex >= self.csr.num_vertices() {
548 return None;
549 }
550
551 let vertex_id = VertexId(self.current_vertex as u32 + self.csr.min_vertex_id);
552 let edges = self.csr.out_edges(vertex_id);
553 self.current_vertex += 1;
554
555 Some((vertex_id, edges))
556 }
557}
558
559pub struct CsrBuilder {
561 adjacency: Vec<Vec<usize>>,
562 coords: Vec<VertexAddr>,
563}
564
565impl Default for CsrBuilder {
566 fn default() -> Self {
567 Self::new()
568 }
569}
570
571impl CsrBuilder {
572 pub fn new() -> Self {
573 Self {
574 adjacency: Vec::new(),
575 coords: Vec::new(),
576 }
577 }
578
579 pub fn add_vertex(&mut self, addr: VertexAddr) -> usize {
581 let idx = self.adjacency.len();
582 self.adjacency.push(Vec::new());
583 self.coords.push(addr);
584 idx
585 }
586
587 pub fn add_edge(&mut self, from: usize, to: usize) {
589 if from < self.adjacency.len() {
590 self.adjacency[from].push(to);
591 }
592 }
593
594 pub fn build(self) -> CsrEdges {
596 let adj: Vec<_> = self
598 .adjacency
599 .into_iter()
600 .enumerate()
601 .map(|(idx, edges)| (idx as u32, edges.into_iter().map(|e| e as u32).collect()))
602 .collect();
603 CsrEdges::from_adjacency(adj, &self.coords)
604 }
605}