legume_numeric/leiden/
network.rs1use crate::leiden::Clustering;
4use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
5use rayon::slice::ParallelSlice;
6use rustc_hash::FxHashMap as HashMap;
7
8pub struct Network {
14 adj: Vec<Vec<(u32, f32)>>,
17 node_weights: Vec<f32>,
19 edge_count: usize,
21}
22
23pub struct NeighborAndWeightIter<'a> {
25 iter: std::slice::Iter<'a, (u32, f32)>,
26}
27
28impl Iterator for NeighborAndWeightIter<'_> {
29 type Item = (usize, f64);
30
31 fn next(&mut self) -> Option<Self::Item> {
32 self.iter.next().map(|&(n, w)| (n as usize, f64::from(w)))
33 }
34}
35
36pub struct EdgeReferences<'a> {
40 adj: &'a [Vec<(u32, f32)>],
41 src: usize,
42 pos: usize,
43}
44
45impl Iterator for EdgeReferences<'_> {
46 type Item = (usize, usize, f32);
47
48 fn next(&mut self) -> Option<Self::Item> {
49 while self.src < self.adj.len() {
50 let edges = &self.adj[self.src];
51 while self.pos < edges.len() {
52 let (tgt32, w) = edges[self.pos];
53 self.pos += 1;
54 let tgt = tgt32 as usize;
55 if tgt >= self.src {
56 return Some((self.src, tgt, w));
57 }
58 }
59 self.src += 1;
60 self.pos = 0;
61 }
62 None
63 }
64}
65
66impl Network {
67 #[must_use]
69 pub fn new() -> Network {
70 Network::with_capacity(0)
71 }
72
73 #[must_use]
75 pub fn with_capacity(n_nodes: usize) -> Network {
76 Network {
77 adj: Vec::with_capacity(n_nodes),
78 node_weights: Vec::with_capacity(n_nodes),
79 edge_count: 0,
80 }
81 }
82
83 #[must_use]
86 pub fn with_nodes(node_weights: &[f32], degrees: &[usize]) -> Network {
87 assert_eq!(
88 node_weights.len(),
89 degrees.len(),
90 "one degree per node weight"
91 );
92 Network {
93 adj: degrees.iter().map(|&d| Vec::with_capacity(d)).collect(),
94 node_weights: node_weights.to_vec(),
95 edge_count: 0,
96 }
97 }
98
99 pub fn add_node(&mut self, weight: f32) -> usize {
101 let id = self.node_weights.len();
102 self.node_weights.push(weight);
103 self.adj.push(Vec::new());
104 id
105 }
106
107 pub fn add_edge(&mut self, source: usize, target: usize, weight: f32) {
112 let s32 = u32::try_from(source).expect("node index exceeds u32::MAX");
113 let t32 = u32::try_from(target).expect("node index exceeds u32::MAX");
114 self.adj[source].push((t32, weight));
115 self.adj[target].push((s32, weight));
116 self.edge_count += 1;
117 }
118
119 #[must_use]
121 pub fn nodes(&self) -> usize {
122 self.node_weights.len()
123 }
124
125 #[must_use]
127 pub fn edge_count(&self) -> usize {
128 self.edge_count
129 }
130
131 #[must_use]
136 pub fn weight(&self, node: usize) -> f64 {
137 f64::from(self.node_weights[node])
138 }
139
140 pub fn node_weight_mut(&mut self, node: usize) -> &mut f32 {
145 &mut self.node_weights[node]
146 }
147
148 #[must_use]
153 pub fn neighbors(&self, node: usize) -> NeighborAndWeightIter<'_> {
154 NeighborAndWeightIter {
155 iter: self.adj[node].iter(),
156 }
157 }
158
159 pub fn edge_references(&self) -> EdgeReferences<'_> {
161 EdgeReferences {
162 adj: &self.adj,
163 src: 0,
164 pos: 0,
165 }
166 }
167
168 #[must_use]
170 pub fn get_total_node_weight(&self) -> f64 {
171 let mut w = 0.0;
172 for i in 0..self.nodes() {
173 w += self.weight(i);
174 }
175 w
176 }
177
178 #[must_use]
180 pub fn get_total_edge_weight(&self) -> f64 {
181 let mut s = 0.0;
182 for (src, edges) in self.adj.iter().enumerate() {
183 for &(t, w) in edges {
184 if t as usize >= src {
185 s += f64::from(w);
186 }
187 }
188 }
189 s
190 }
191
192 #[must_use]
195 pub fn get_total_edge_weight_par(&self) -> f64 {
196 let mut partial_sums = vec![];
197
198 self.adj
199 .par_chunks(256)
200 .map(|chunk| {
201 chunk
202 .iter()
203 .map(|edges| edges.iter().fold(0.0, |a, &(_, w)| a + f64::from(w)))
204 .sum::<f64>()
205 })
206 .collect_into_vec(&mut partial_sums);
207
208 partial_sums.iter().sum::<f64>() / 2.0
209 }
210
211 pub fn get_total_edge_weight_per_node(&self, result: &mut Vec<f64>) {
213 result.clear();
214 for edges in &self.adj {
215 let w = edges.iter().fold(0.0, |a, &(_, w)| a + f64::from(w));
216 result.push(w);
217 }
218 }
219
220 #[must_use]
229 pub fn create_reduced_network(&self, clustering: &impl Clustering) -> Network {
230 let mut cluster_g = Network::with_capacity(clustering.num_clusters());
231
232 for _ in 0..clustering.num_clusters() {
233 cluster_g.add_node(0.0);
234 }
235
236 for n in 0..self.nodes() {
237 let cluster = clustering.get(n);
238 cluster_g.node_weights[cluster] += self.node_weights[n];
239 }
240
241 let mut edge_memo: HashMap<(u32, u32), f32> = HashMap::default();
242
243 for (src, tgt, w) in self.edge_references() {
244 let c1 = u32::try_from(clustering.get(src)).expect("cluster id exceeds u32::MAX");
245 let c2 = u32::try_from(clustering.get(tgt)).expect("cluster id exceeds u32::MAX");
246
247 if c1 == c2 {
248 continue;
249 }
250
251 let (mn, mx) = if c1 < c2 { (c1, c2) } else { (c2, c1) };
252 *edge_memo.entry((mn, mx)).or_insert(0.0) += w;
253 }
254
255 for (&(c1, c2), &weight) in &edge_memo {
256 cluster_g.add_edge(c1 as usize, c2 as usize, weight);
257 }
258
259 cluster_g
260 }
261
262 pub fn create_subnetworks(&self, c: &impl Clustering) -> Vec<Network> {
267 let mut graphs: Vec<Network> = (0..c.num_clusters())
268 .map(|_| Network::with_capacity(0))
269 .collect();
270 let mut new_id_map = Vec::with_capacity(c.nodes());
271 let mut counts = vec![0usize; c.num_clusters()];
272
273 for i in 0..self.nodes() {
274 let cluster = c.get(i);
275 let new_id = counts[cluster];
276 new_id_map.push(new_id);
277 counts[cluster] += 1;
278 graphs[cluster].add_node(self.node_weights[i]);
279 }
280
281 for (n1, n2, w) in self.edge_references() {
282 let c1 = c.get(n1);
283 let c2 = c.get(n2);
284 if c1 == c2 {
285 graphs[c1].add_edge(new_id_map[n1], new_id_map[n2], w);
286 }
287 }
288
289 graphs
290 }
291}
292
293impl Default for Network {
294 fn default() -> Self {
295 Self::new()
296 }
297}