Merge the mutable buffer into the dense CSR arrays. Called during idle periods. Rebuilds the contiguous offset/target/label (and weight) arrays from scratch (buffer + surviving dense edges). The old arrays are dropped, freeing memory. O(E) where E = total edges. # Errors Returns [`GraphError::MemoryBudget`] if a memory governor is installed and the dense-array allocation would exceed the `Graph
(&mut self)
| 17 | /// Returns [`GraphError::MemoryBudget`] if a memory governor is installed |
| 18 | /// and the dense-array allocation would exceed the `Graph` engine budget. |
| 19 | pub fn compact(&mut self) -> Result<(), GraphError> { |
| 20 | let n = self.id_to_node.len(); |
| 21 | let mut new_out_edges: Vec<Vec<(u32, u32)>> = vec![Vec::new(); n]; |
| 22 | let mut new_in_edges: Vec<Vec<(u32, u32)>> = vec![Vec::new(); n]; |
| 23 | let mut new_out_weights: Vec<Vec<f64>> = if self.has_weights { |
| 24 | vec![Vec::new(); n] |
| 25 | } else { |
| 26 | Vec::new() |
| 27 | }; |
| 28 | let mut new_in_weights: Vec<Vec<f64>> = if self.has_weights { |
| 29 | vec![Vec::new(); n] |
| 30 | } else { |
| 31 | Vec::new() |
| 32 | }; |
| 33 | |
| 34 | // Collect surviving dense edges. |
| 35 | for node in 0..n { |
| 36 | let node_id = node as u32; |
| 37 | let idx = node_id as usize; |
| 38 | |
| 39 | // Outbound dense edges. |
| 40 | if idx + 1 < self.out_offsets.len() { |
| 41 | let start = self.out_offsets[idx] as usize; |
| 42 | let end = self.out_offsets[idx + 1] as usize; |
| 43 | for i in start..end { |
| 44 | let lid = self.out_labels[i]; |
| 45 | let dst = self.out_targets[i]; |
| 46 | if !self.deleted_edges.contains(&(node_id, lid, dst)) { |
| 47 | new_out_edges[node].push((lid, dst)); |
| 48 | if self.has_weights { |
| 49 | let w = self |
| 50 | .out_weights |
| 51 | .as_ref() |
| 52 | .map_or(1.0, |ws| ws.get(i).copied().unwrap_or(1.0)); |
| 53 | new_out_weights[node].push(w); |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | // Inbound dense edges. |
| 60 | if idx + 1 < self.in_offsets.len() { |
| 61 | let start = self.in_offsets[idx] as usize; |
| 62 | let end = self.in_offsets[idx + 1] as usize; |
| 63 | for i in start..end { |
| 64 | let lid = self.in_labels[i]; |
| 65 | let src = self.in_targets[i]; |
| 66 | if !self.deleted_edges.contains(&(src, lid, node_id)) { |
| 67 | new_in_edges[node].push((lid, src)); |
| 68 | if self.has_weights { |
| 69 | let w = self |
| 70 | .in_weights |
| 71 | .as_ref() |
| 72 | .map_or(1.0, |ws| ws.get(i).copied().unwrap_or(1.0)); |
| 73 | new_in_weights[node].push(w); |
| 74 | } |
| 75 | } |
| 76 | } |