graphix/
graphix.rs

1use std::collections::HashMap;
2
3pub struct GraphRep<K> {
4    v: Vec<usize>,
5    //src: Vec<usize>,
6    e: Vec<(usize, K, usize)>,
7    //w: Vec<(usize, K)>,
8    pub id: Vec<(usize, usize, K)>,
9    pub mapping: Vec<Vec<usize>>, //maps current verts to original verts
10}
11
12impl<K: PartialOrd + Copy> GraphRep<K> {
13    pub fn edges_from(&self, vertex: usize) -> &[(usize, K, usize)] {
14        if vertex + 1 >= self.v.len() {
15            panic!(
16                "edges_from(): vertex {} out of range (v.len() = {})",
17                vertex,
18                self.v.len()
19            );
20        }
21        let edges_start = self.v[vertex];
22        let edges_end = self.v[vertex + 1];
23
24        &self.e[edges_start..edges_end]
25    }
26
27    pub fn original_edge(&self, edge_id: usize) -> Option<&(usize, usize, K)> {
28        self.id.get(edge_id)
29    }
30
31    pub fn num_vertices(&self) -> usize {
32        self.v.len() - 1
33    }
34
35    pub fn num_edges(&self) -> usize {
36        self.e.len() / 2
37    }
38
39    pub fn v_len(&self) -> usize {
40        self.v.len()
41    }
42
43    pub fn e_len(&self) -> usize {
44        self.e.len()
45    }
46
47    //function that can create a graph from a vec<(vertex, vertex, weight)>
48    pub fn from_list(edges: Vec<(usize, usize, K)>) -> Self {
49        let m = edges.len();
50        if m == 0 {
51            // zero vertices, zero edges, empty id
52            return GraphRep {
53                v: vec![0], // one offset, so edges_from(u) is never OOB
54                e: Vec::new(),
55                id: Vec::new(),
56                mapping: Vec::new(),
57            };
58        }
59        let id = edges;
60        let n = id
61            .iter()
62            .flat_map(|&(u, v, _)| [u, v])
63            .max()
64            .map_or(0, |mx| mx + 1);
65
66        // Initialize mapping: each vertex maps to itself
67        let mapping = (0..n).map(|i| vec![i]).collect();
68
69        // number of edges at every vertex
70        let mut v = vec![0; n + 1];
71        for &(u, vtx, _) in &id {
72            v[u] += 1;
73            v[vtx] += 1;
74        }
75
76        // 3) fused prefix‐sum into `v` *and* init `write_cursor`
77        let mut write_cursor = Vec::with_capacity(v.len());
78        let mut running_sum = 0;
79        for slot in &mut v {
80            let deg = *slot; // old count
81            write_cursor.push(running_sum);
82            *slot = running_sum; // now CSR offset
83            running_sum += deg;
84        }
85
86        // 4) allocate and scatter into `e`
87        let dummy = (0, id[0].2, 0);
88        let mut e = vec![dummy; 2 * m];
89        for (edge_id, &(src, dst, weight)) in id.iter().enumerate() {
90            // half‐edge src → dst
91            let pos = write_cursor[src];
92            e[pos] = (dst, weight, edge_id);
93            write_cursor[src] += 1;
94
95            // half‐edge dst → src
96            let pos_back = write_cursor[dst];
97            e[pos_back] = (src, weight, edge_id);
98            write_cursor[dst] += 1;
99        }
100
101        GraphRep { v, e, id, mapping }
102    }
103
104    pub fn update_v_e(&mut self, edges: &[(usize, usize, K, usize)]) {
105        if edges.is_empty() {
106            self.v = vec![0]; // no vertices left
107            self.e.clear(); // clear edge list
108            self.mapping.clear();
109            return; // exit early
110        }
111
112        // 1. Determine new supernodes from current mapping
113        let mut supernode_map: HashMap<usize, Vec<usize>> = HashMap::new();
114        for (idx, originals) in self.mapping.iter().enumerate() {
115            supernode_map.entry(idx).or_insert(originals.clone());
116        }
117
118        // 2. Rebuild mapping for contracted graph
119        let new_mapping = Vec::new();
120
121        // Determine number of vertices (based on max index seen in edge list)
122        let n = edges
123            .iter()
124            .flat_map(|&(u, v, _, _)| [u, v])
125            .max()
126            .unwrap_or(0)
127            + 1;
128
129        // Count the degree of each vertex to prepare CSR offsets
130        let mut deg = vec![0; n + 1];
131        for &(u, v, _, _) in edges {
132            deg[u] += 1;
133            deg[v] += 1;
134        }
135
136        // Compute prefix sums over degrees to build CSR offset vector `v`
137        let mut v = vec![0; n + 1];
138        for i in 1..=n {
139            v[i] = v[i - 1] + deg[i - 1];
140        }
141        // Clone `v` as write cursor to track where to insert each neighbor
142        let mut cursor = v.clone();
143
144        // Allocate edge list of twice the input size (undirected = 2 half-edges)
145        let mut e = vec![(0, edges[0].2, 0); 2 * edges.len()];
146
147        // Scatter edges into CSR structure using cursor positions
148        for &(u, vtx, w, id) in edges {
149            e[cursor[u]] = (vtx, w, id);
150            cursor[u] += 1;
151
152            e[cursor[vtx]] = (u, w, id);
153            cursor[vtx] += 1;
154        }
155
156        // Replace current CSR layout with newly built one
157        self.v = v;
158        self.e = e;
159        self.mapping = new_mapping;
160    }
161
162    ///returns all original edges
163    pub fn all_edges(&self) -> Vec<(usize, usize, K, usize)> {
164        let mut result = Vec::new();
165
166        for (eid, &(u, v, w)) in self.id.iter().enumerate() {
167            if u < v {
168                result.push((u, v, w, eid));
169            } else {
170                result.push((v, u, w, eid));
171            }
172        }
173        result
174    }
175
176    pub fn current_edges(&self) -> Vec<(usize, usize, K, usize)> {
177        let mut out = Vec::with_capacity(self.num_edges()); // m edges
178        let n = self.num_vertices();
179
180        for u in 0..n {
181            for &(v, w, eid) in self.edges_from(u) {
182                if u < v {
183                    // keep one direction only
184                    out.push((u, v, w, eid));
185                }
186            }
187        }
188        out
189    }
190
191    // pub fn contract_cc(&mut self, cc_ids: &[isize]) {
192    //     // old number of vertices and number of supernodes
193    //     let org_vert_count = self.num_vertices();
194    //     debug_assert_eq!(cc_ids.len(), org_vert_count);
195
196    //     let supernode_count = cc_ids
197    //         .iter()
198    //         .map(|&label| label as usize)
199    //         .max()
200    //         .unwrap_or(0)
201    //         + 1;
202
203    //     //create a new offsett array (v), and couint half edges
204    //     let mut new_offsets = vec![0usize; supernode_count + 1];
205
206    //     for old_vert_index in 0..org_vert_count {
207    //         let super_node_id = cc_ids[old_vert_index] as usize;
208    //         for &(neighbor_old_index, _edge_weight, _org_edge_id) in self.edges_from(old_vert_index)
209    //         {
210    //             let neighbor_super_id = cc_ids[neighbor_old_index] as usize;
211
212    //             if super_node_id != neighbor_super_id {
213    //                 new_offsets[super_node_id + 1] += 1;
214    //             }
215    //         }
216    //     }
217
218    //     for i in 1..=supernode_count {
219    //         new_offsets[i] += new_offsets[i - 1];
220    //     }
221
222    //     let total_edges = new_offsets[supernode_count];
223    //     let dummy_edges = (0, self.e[0].1, 0);
224    //     let mut new_edges = vec![dummy_edges; total_edges];
225
226    //     let mut write_cursor = new_offsets.clone();
227
228    //     for old_vert_index in 0..org_vert_count {
229    //         let super_node_id = cc_ids[old_vert_index] as usize;
230    //         for &(neighbor_old_index, edge_weight, org_edge_id) in self.edges_from(old_vert_index) {
231    //             let neighbor_super_id = cc_ids[neighbor_old_index] as usize;
232    //             if super_node_id != neighbor_super_id {
233    //                 let write_pos = write_cursor[super_node_id];
234    //                 new_edges[write_pos] = (neighbor_super_id, edge_weight, org_edge_id);
235    //                 write_cursor[super_node_id] += 1;
236    //             }
237    //         }
238    //     }
239    //     self.v = new_offsets;
240    //     self.e = new_edges
241    // }
242}
243
244// #[cfg(test)]
245// mod tests {
246//     use super::GraphRep;
247
248//     #[test]
249//     fn test_empty() {
250//         let g: GraphRep<i32> = GraphRep::from_list(Vec::new());
251//         assert_eq!(g.num_vertices(), 0);
252//         assert_eq!(g.num_edges(), 0);
253//         assert!(g.id.is_empty());
254//     }
255
256//     #[test]
257//     fn test_small_graph() {
258//         // edges = [(0–1,1), (1–2,2), (2–0,3)]
259//         let edges = vec![(0, 1, 1), (1, 2, 2), (2, 0, 3)];
260//         let g = GraphRep::from_list(edges.clone());
261
262//         assert_eq!(g.num_vertices(), 3);
263//         assert_eq!(g.num_edges(), 3);
264//         assert_eq!(g.id, edges);
265
266//         let degs: Vec<_> = (0..3).map(|u| g.edges_from(u).len()).collect();
267//         assert_eq!(degs, vec![2, 2, 2]);
268
269//         let mut adj0: Vec<_> = g.edges_from(0).iter().cloned().collect();
270//         adj0.sort_by_key(|&(to, _, eid)| (to, eid));
271//         assert_eq!(adj0, vec![(1, 1, 0), (2, 3, 2)]);
272//     }
273// }
274
275// #[cfg(test)]
276// mod contract_tests {
277//     use super::GraphRep;
278
279//     #[test]
280//     fn test_contract_cc_chain() {
281//         // Original graph: a path 0–1–2–3 with weights 10, 20, 30
282//         let edges = vec![(0, 1, 10), (1, 2, 20), (2, 3, 30)];
283//         let mut g: GraphRep<i32> = GraphRep::from_list(edges.clone());
284
285//         // Verify original graph structure
286//         assert_eq!(g.num_vertices(), 4);
287//         assert_eq!(g.num_edges(), 3);
288
289//         // Define CC IDs that merge {0,1} into super-node 0 and {2,3} into super-node 1
290//         let cc_ids = vec![0isize, 0, 1, 1];
291
292//         // Contract in-place
293//         g.contract_cc(&cc_ids);
294
295//         // After contraction we expect:
296//         // - 2 super-nodes (0 and 1)
297//         // - 1 undirected edge between them (the former 1–2 edge)
298//         assert_eq!(g.num_vertices(), 2);
299//         assert_eq!(g.num_edges(), 1);
300
301//         // CSR offsets should be [0, 1, 2]:
302//         //  node 0 has 1 half-edge, node 1 has 1 half-edge
303//         assert_eq!(g.v, vec![0, 1, 2]);
304
305//         // The only remaining edge should have:
306//         //  - from super-node 0 → 1, weight 20, orig_eid = 1
307//         let out0 = g.edges_from(0);
308//         assert_eq!(out0.len(), 1);
309//         assert_eq!(out0[0], (1, 20, 1));
310
311//         // And the symmetric half-edge from 1 → 0
312//         let out1 = g.edges_from(1);
313//         assert_eq!(out1.len(), 1);
314//         assert_eq!(out1[0], (0, 20, 1));
315//     }
316
317//     #[test]
318//     fn test_contract_cc_identity() {
319//         // If cc_ids is [0,1,2,3], nothing should change
320//         let edges = vec![(0, 1, 5), (1, 2, 6), (2, 3, 7)];
321//         let mut g1: GraphRep<i32> = GraphRep::from_list(edges.clone());
322//         let original_v = g1.v.clone();
323//         let original_e = g1.e.clone();
324
325//         let cc_ids = vec![0isize, 1, 2, 3];
326//         g1.contract_cc(&cc_ids);
327
328//         // Vertex and edge counts unchanged
329//         assert_eq!(g1.num_vertices(), 4);
330//         assert_eq!(g1.num_edges(), 3);
331//         // CSR arrays identical
332//         assert_eq!(g1.v, original_v);
333//         assert_eq!(g1.e, original_e);
334//     }
335// }