quantrs2_tytan/analysis/
graph.rs1use scirs2_core::random::rngs::StdRng;
7use scirs2_core::random::{Rng, SeedableRng};
8use std::collections::HashSet;
9
10pub fn generate_graph(
12 n_nodes: usize,
13 edge_probability: f64,
14) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
15 if !(0.0..=1.0).contains(&edge_probability) {
16 return Err("Edge probability must be between 0 and 1".into());
17 }
18
19 let mut rng = StdRng::seed_from_u64(42);
20 let mut edges = Vec::new();
21
22 for i in 0..n_nodes {
24 for j in (i + 1)..n_nodes {
25 if rng.gen::<f64>() < edge_probability {
26 edges.push((i, j));
27 }
28 }
29 }
30
31 Ok(edges)
32}
33
34pub fn generate_complete_graph(n_nodes: usize) -> Vec<(usize, usize)> {
36 let mut edges = Vec::new();
37
38 for i in 0..n_nodes {
39 for j in (i + 1)..n_nodes {
40 edges.push((i, j));
41 }
42 }
43
44 edges
45}
46
47pub fn generate_cycle_graph(n_nodes: usize) -> Vec<(usize, usize)> {
49 let mut edges = Vec::new();
50
51 if n_nodes < 3 {
52 return edges;
53 }
54
55 for i in 0..n_nodes {
57 edges.push((i, (i + 1) % n_nodes));
58 }
59
60 edges
61}
62
63pub fn generate_grid_graph(rows: usize, cols: usize) -> Vec<(usize, usize)> {
65 let mut edges = Vec::new();
66
67 let node_index = |r, c| r * cols + c;
69
70 for r in 0..rows {
72 for c in 0..(cols - 1) {
73 edges.push((node_index(r, c), node_index(r, c + 1)));
74 }
75 }
76
77 for r in 0..(rows - 1) {
79 for c in 0..cols {
80 edges.push((node_index(r, c), node_index(r + 1, c)));
81 }
82 }
83
84 edges
85}
86
87pub fn generate_bipartite_graph(
89 left_nodes: usize,
90 right_nodes: usize,
91 edge_probability: f64,
92) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
93 if !(0.0..=1.0).contains(&edge_probability) {
94 return Err("Edge probability must be between 0 and 1".into());
95 }
96
97 let mut rng = StdRng::seed_from_u64(42);
98 let mut edges = Vec::new();
99
100 for i in 0..left_nodes {
102 for j in 0..right_nodes {
103 if rng.gen::<f64>() < edge_probability {
104 edges.push((i, left_nodes + j));
105 }
106 }
107 }
108
109 Ok(edges)
110}
111
112pub fn generate_regular_graph(
114 n_nodes: usize,
115 degree: usize,
116) -> Result<Vec<(usize, usize)>, Box<dyn std::error::Error>> {
117 if degree >= n_nodes {
118 return Err("Degree must be less than number of nodes".into());
119 }
120
121 if (n_nodes * degree) % 2 != 0 {
122 return Err("n_nodes * degree must be even for regular graph".into());
123 }
124
125 let mut rng = StdRng::seed_from_u64(42);
126 let mut edges = HashSet::new();
127 let mut node_degrees = vec![0; n_nodes];
128
129 let max_attempts = n_nodes * degree * 10;
131 let mut attempts = 0;
132
133 while node_degrees.iter().any(|&d| d < degree) && attempts < max_attempts {
134 attempts += 1;
135
136 let available: Vec<usize> = (0..n_nodes).filter(|&i| node_degrees[i] < degree).collect();
138
139 if available.len() < 2 {
140 continue;
141 }
142
143 let i = available[rng.gen_range(0..available.len())];
145 let j = available[rng.gen_range(0..available.len())];
146
147 if i != j && !edges.contains(&(i.min(j), i.max(j))) {
148 edges.insert((i.min(j), i.max(j)));
149 node_degrees[i] += 1;
150 node_degrees[j] += 1;
151 }
152 }
153
154 if node_degrees.iter().any(|&d| d < degree) {
155 return Err("Failed to generate regular graph with given parameters".into());
156 }
157
158 Ok(edges.into_iter().collect())
159}
160
161pub fn generate_tree_graph(n_nodes: usize) -> Vec<(usize, usize)> {
163 let mut rng = StdRng::seed_from_u64(42);
164 let mut edges = Vec::new();
165
166 if n_nodes == 0 {
167 return edges;
168 }
169
170 for i in 1..n_nodes {
173 let parent = rng.gen_range(0..i);
174 edges.push((parent, i));
175 }
176
177 edges
178}
179
180pub struct GraphProperties {
182 pub n_nodes: usize,
183 pub n_edges: usize,
184 pub avg_degree: f64,
185 pub max_degree: usize,
186 pub min_degree: usize,
187 pub is_connected: bool,
188 pub density: f64,
189}
190
191pub fn analyze_graph(n_nodes: usize, edges: &[(usize, usize)]) -> GraphProperties {
193 let mut degrees = vec![0; n_nodes];
194
195 for (i, j) in edges {
196 degrees[*i] += 1;
197 degrees[*j] += 1;
198 }
199
200 let avg_degree = degrees.iter().sum::<usize>() as f64 / n_nodes as f64;
201 let max_degree = *degrees.iter().max().unwrap_or(&0);
202 let min_degree = *degrees.iter().min().unwrap_or(&0);
203
204 let is_connected = if n_nodes > 0 {
206 let mut visited = vec![false; n_nodes];
207 let mut queue = vec![0];
208 visited[0] = true;
209
210 while let Some(node) = queue.pop() {
211 for (i, j) in edges {
212 if *i == node && !visited[*j] {
213 visited[*j] = true;
214 queue.push(*j);
215 } else if *j == node && !visited[*i] {
216 visited[*i] = true;
217 queue.push(*i);
218 }
219 }
220 }
221
222 visited.iter().all(|&v| v)
223 } else {
224 true
225 };
226
227 let max_edges = n_nodes * (n_nodes - 1) / 2;
228 let density = if max_edges > 0 {
229 edges.len() as f64 / max_edges as f64
230 } else {
231 0.0
232 };
233
234 GraphProperties {
235 n_nodes,
236 n_edges: edges.len(),
237 avg_degree,
238 max_degree,
239 min_degree,
240 is_connected,
241 density,
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn test_complete_graph() {
251 let edges = generate_complete_graph(5);
252 assert_eq!(edges.len(), 10); }
254
255 #[test]
256 fn test_cycle_graph() {
257 let edges = generate_cycle_graph(5);
258 assert_eq!(edges.len(), 5);
259 }
260
261 #[test]
262 fn test_grid_graph() {
263 let edges = generate_grid_graph(3, 3);
264 assert_eq!(edges.len(), 12);
266 }
267
268 #[test]
269 fn test_tree_graph() {
270 let edges = generate_tree_graph(10);
271 assert_eq!(edges.len(), 9); }
273}