rustkernel_graph/
types.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct CsrGraph {
10 pub num_nodes: usize,
12 pub num_edges: usize,
14 pub row_offsets: Vec<u64>,
16 pub col_indices: Vec<u64>,
18 pub weights: Option<Vec<f32>>,
20}
21
22impl CsrGraph {
23 #[must_use]
25 pub fn empty() -> Self {
26 Self {
27 num_nodes: 0,
28 num_edges: 0,
29 row_offsets: vec![0],
30 col_indices: Vec::new(),
31 weights: None,
32 }
33 }
34
35 #[must_use]
37 pub fn from_edges(num_nodes: usize, edges: &[(u64, u64)]) -> Self {
38 let mut row_counts = vec![0u64; num_nodes];
39 for (src, _) in edges {
40 row_counts[*src as usize] += 1;
41 }
42
43 let mut row_offsets = vec![0u64; num_nodes + 1];
44 for i in 0..num_nodes {
45 row_offsets[i + 1] = row_offsets[i] + row_counts[i];
46 }
47
48 let mut col_indices = vec![0u64; edges.len()];
49 let mut current_pos = row_offsets.clone();
50
51 for (src, dst) in edges {
52 let pos = current_pos[*src as usize] as usize;
53 col_indices[pos] = *dst;
54 current_pos[*src as usize] += 1;
55 }
56
57 Self {
58 num_nodes,
59 num_edges: edges.len(),
60 row_offsets,
61 col_indices,
62 weights: None,
63 }
64 }
65
66 #[must_use]
68 pub fn out_degree(&self, node: u64) -> u64 {
69 let n = node as usize;
70 if n >= self.num_nodes {
71 return 0;
72 }
73 self.row_offsets[n + 1] - self.row_offsets[n]
74 }
75
76 #[must_use]
78 pub fn neighbors(&self, node: u64) -> &[u64] {
79 let n = node as usize;
80 if n >= self.num_nodes {
81 return &[];
82 }
83 let start = self.row_offsets[n] as usize;
84 let end = self.row_offsets[n + 1] as usize;
85 &self.col_indices[start..end]
86 }
87
88 #[must_use]
90 pub fn is_weighted(&self) -> bool {
91 self.weights.is_some()
92 }
93
94 #[must_use]
96 pub fn density(&self) -> f64 {
97 if self.num_nodes <= 1 {
98 return 0.0;
99 }
100 let max_edges = self.num_nodes * (self.num_nodes - 1);
101 self.num_edges as f64 / max_edges as f64
102 }
103}
104
105#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
107pub struct NodeScore {
108 pub node_id: u64,
110 pub score: f64,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct CentralityResult {
117 pub scores: Vec<NodeScore>,
119 pub iterations: Option<u32>,
121 pub converged: bool,
123}
124
125impl CentralityResult {
126 #[must_use]
128 pub fn top_k(&self, k: usize) -> Vec<NodeScore> {
129 let mut sorted = self.scores.clone();
130 sorted.sort_by(|a, b| {
131 b.score
132 .partial_cmp(&a.score)
133 .unwrap_or(std::cmp::Ordering::Equal)
134 });
135 sorted.truncate(k);
136 sorted
137 }
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct CommunityResult {
143 pub assignments: Vec<u64>,
145 pub num_communities: usize,
147 pub modularity: f64,
149}
150
151#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
153pub struct SimilarityScore {
154 pub id_a: u64,
156 pub id_b: u64,
158 pub similarity: f64,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct SimilarityResult {
165 pub scores: Vec<SimilarityScore>,
167 pub method: String,
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_csr_from_edges() {
177 let edges = vec![(0, 1), (0, 2), (1, 2), (2, 0)];
178 let graph = CsrGraph::from_edges(3, &edges);
179
180 assert_eq!(graph.num_nodes, 3);
181 assert_eq!(graph.num_edges, 4);
182 assert_eq!(graph.out_degree(0), 2);
183 assert_eq!(graph.out_degree(1), 1);
184 assert_eq!(graph.out_degree(2), 1);
185 }
186
187 #[test]
188 fn test_graph_density() {
189 let edges = vec![(0, 1), (1, 0), (0, 2), (2, 0), (1, 2), (2, 1)];
190 let graph = CsrGraph::from_edges(3, &edges);
191
192 assert!((graph.density() - 1.0).abs() < 0.001);
194 }
195}