graph_api_benches/
generators.rs

1use graph_api_lib::Graph;
2use graph_api_test::{Edge, Language, Project, Refs, Vertex, populate_graph};
3use rand::prelude::*;
4use rand::rngs::StdRng;
5use rand::{Rng, SeedableRng};
6use std::collections::HashSet;
7use uuid::Uuid;
8
9/// Graph size configurations
10#[derive(Clone, Copy)]
11pub enum GraphSize {
12    Small,  // ~100 vertices, ~300 edges
13    Medium, // ~1,000 vertices, ~3,000 edges
14    Large,  // ~10,000 vertices, ~30,000 edges
15    Huge,   // ~100,000 vertices, ~300,000 edges
16}
17
18impl GraphSize {
19    pub fn vertex_count(&self) -> usize {
20        match self {
21            GraphSize::Small => 100,
22            GraphSize::Medium => 1_000,
23            GraphSize::Large => 10_000,
24            GraphSize::Huge => 100_000,
25        }
26    }
27
28    pub fn edge_multiplier(&self) -> usize {
29        3 // Each vertex has on average 3 edges
30    }
31}
32
33/// Generates a simple populated graph with the standard test data
34pub fn generate_test_graph<G>(graph: &mut G) -> Refs<G>
35where
36    G: Graph<Vertex = Vertex, Edge = Edge>,
37{
38    populate_graph(graph)
39}
40
41/// Generates a random graph with the specified size
42pub fn generate_random_graph<G>(graph: &mut G, size: GraphSize, seed: u64) -> Vec<G::VertexId>
43where
44    G: Graph<Vertex = Vertex, Edge = Edge>,
45{
46    let mut rng = StdRng::seed_from_u64(seed);
47    let vertex_count = size.vertex_count();
48    let edge_count = vertex_count * size.edge_multiplier();
49
50    // Generate vertices
51    let mut vertex_ids = Vec::with_capacity(vertex_count);
52    for i in 0..vertex_count {
53        let vertex = match rng.random_range(0..3) {
54            0 => Vertex::Person {
55                name: format!("Person-{}", i),
56                age: rng.random_range(18..80),
57                unique_id: Uuid::new_v4(),
58                username: format!("user_{}", i),
59                biography: format!("Bio for person {}: {}", i, random_biography(&mut rng)),
60            },
61            1 => Vertex::Project(Project {
62                name: format!("Project-{}", i),
63            }),
64            _ => Vertex::Rust,
65        };
66
67        vertex_ids.push(graph.add_vertex(vertex));
68    }
69
70    // Generate edges
71    let mut added_edges = HashSet::new();
72    for _ in 0..edge_count {
73        // Pick random source and target vertices
74        let src_idx = rng.random_range(0..vertex_count);
75        let tgt_idx = rng.random_range(0..vertex_count);
76        let src = vertex_ids[src_idx];
77        let tgt = vertex_ids[tgt_idx];
78
79        // Skip self-loops and duplicates
80        if src == tgt || added_edges.contains(&(src, tgt)) {
81            continue;
82        }
83
84        // Add edge with random type
85        let edge = match rng.random_range(0..3) {
86            0 => Edge::Knows {
87                since: rng.random_range(1980..2023),
88            },
89            1 => Edge::Created,
90            _ => Edge::Language(Language {
91                name: match rng.random_range(0..4) {
92                    0 => "Rust".to_string(),
93                    1 => "Java".to_string(),
94                    2 => "Python".to_string(),
95                    _ => "JavaScript".to_string(),
96                },
97            }),
98        };
99
100        graph.add_edge(src, tgt, edge);
101        added_edges.insert((src, tgt));
102    }
103
104    vertex_ids
105}
106
107/// Generates a social network like graph
108pub fn generate_social_graph<G>(graph: &mut G, size: GraphSize, seed: u64) -> Vec<G::VertexId>
109where
110    G: Graph<Vertex = Vertex, Edge = Edge>,
111{
112    let mut rng = StdRng::seed_from_u64(seed);
113    let vertex_count = size.vertex_count();
114
115    // Generate vertices (all people)
116    let mut vertex_ids = Vec::with_capacity(vertex_count);
117    for i in 0..vertex_count {
118        let vertex = Vertex::Person {
119            name: format!("Person-{}", i),
120            age: rng.random_range(18..80),
121            unique_id: Uuid::new_v4(),
122            username: format!("user_{}", i),
123            biography: format!("Social network user {}", i),
124        };
125
126        vertex_ids.push(graph.add_vertex(vertex));
127    }
128
129    // Create a social network structure - people tend to know people nearby in the list
130    // This creates a more realistic social graph structure than purely random connections
131    for i in 0..vertex_count {
132        // Each person knows 5-15 other people
133        let num_connections = rng.random_range(5..15).min(vertex_count - 1);
134        let mut connections = HashSet::new();
135
136        while connections.len() < num_connections {
137            // 80% chance to connect to someone "nearby" (community structure)
138            let target_idx = if rng.random_bool(0.8) {
139                // Connect to someone nearby in the list (community)
140                let range = 50; // community size
141                let start = i.saturating_sub(range / 2);
142                let end = (i + range / 2).min(vertex_count - 1);
143                rng.random_range(start..=end)
144            } else {
145                // Random connection anywhere
146                rng.random_range(0..vertex_count)
147            };
148
149            // Avoid self-loops
150            if target_idx != i {
151                connections.insert(target_idx);
152            }
153        }
154
155        // Create edges for connections
156        for target_idx in connections {
157            let target = vertex_ids[target_idx];
158            let year = rng.random_range(1980..2023);
159            graph.add_edge(vertex_ids[i], target, Edge::Knows { since: year });
160        }
161    }
162
163    vertex_ids
164}
165
166/// Generate a project dependency graph
167pub fn generate_project_graph<G>(graph: &mut G, size: GraphSize, seed: u64) -> Vec<G::VertexId>
168where
169    G: Graph<Vertex = Vertex, Edge = Edge>,
170{
171    let mut rng = StdRng::seed_from_u64(seed);
172    let project_count = size.vertex_count() / 3; // 1/3 projects
173    let person_count = size.vertex_count() / 3; // 1/3 people
174    let rust_count = size.vertex_count() - project_count - person_count; // 1/3 Rust (language nodes)
175
176    // Generate project vertices
177    let mut project_ids = Vec::with_capacity(project_count);
178    for i in 0..project_count {
179        let vertex = Vertex::Project(Project {
180            name: format!("Project-{}", i),
181        });
182        project_ids.push(graph.add_vertex(vertex));
183    }
184
185    // Generate person vertices
186    let mut person_ids = Vec::with_capacity(person_count);
187    for i in 0..person_count {
188        let vertex = Vertex::Person {
189            name: format!("Developer-{}", i),
190            age: rng.random_range(18..80),
191            unique_id: Uuid::new_v4(),
192            username: format!("dev_{}", i),
193            biography: format!("Developer working on project {}", i % project_count),
194        };
195        person_ids.push(graph.add_vertex(vertex));
196    }
197
198    // Generate language vertices (all Rust in this case)
199    let mut rust_ids = Vec::with_capacity(rust_count);
200    for _ in 0..rust_count {
201        rust_ids.push(graph.add_vertex(Vertex::Rust));
202    }
203
204    // Create project dependencies (project -> project edges)
205    for (i, &project_id) in project_ids.iter().enumerate() {
206        // Each project depends on 0-5 other projects
207        let num_deps = rng.random_range(0..=5).min(project_count - 1);
208        let mut deps = HashSet::new();
209
210        while deps.len() < num_deps {
211            let target_idx = rng.random_range(0..project_count);
212            if target_idx != i {
213                // Avoid self-dependencies
214                deps.insert(target_idx);
215            }
216        }
217
218        // Create dependency edges
219        for target_idx in deps {
220            let target = project_ids[target_idx];
221            graph.add_edge(project_id, target, Edge::Created);
222        }
223    }
224
225    // Create author relationships (person -> project edges)
226    for (i, &person_id) in person_ids.iter().enumerate() {
227        // Each person contributes to 1-3 projects
228        let num_projects = rng.random_range(1..=3).min(project_count);
229        let mut projects = HashSet::new();
230
231        // First project is often related to the person's index (simulates main project)
232        projects.insert(i % project_count);
233
234        // Add more random projects
235        while projects.len() < num_projects {
236            projects.insert(rng.random_range(0..project_count));
237        }
238
239        // Create author edges
240        for &project_idx in &projects {
241            let project_id = project_ids[project_idx];
242            graph.add_edge(person_id, project_id, Edge::Created);
243        }
244    }
245
246    // Create language relationships (project -> language edges)
247    for &project_id in &project_ids {
248        // Each project uses 1-2 languages
249        let num_langs = rng.random_range(1..=2).min(rust_count);
250
251        for _ in 0..num_langs {
252            let lang_idx = rng.random_range(0..rust_count);
253            let lang_id = rust_ids[lang_idx];
254
255            graph.add_edge(
256                project_id,
257                lang_id,
258                Edge::Language(Language {
259                    name: "Rust".to_string(),
260                }),
261            );
262        }
263    }
264
265    // Combine all vertex IDs and return
266    let mut all_vertices = Vec::with_capacity(size.vertex_count());
267    all_vertices.extend(project_ids);
268    all_vertices.extend(person_ids);
269    all_vertices.extend(rust_ids);
270    all_vertices
271}
272
273/// Helper to generate random biography text
274fn random_biography(rng: &mut StdRng) -> String {
275    let adjectives = [
276        "creative",
277        "diligent",
278        "innovative",
279        "experienced",
280        "passionate",
281    ];
282    let roles = ["developer", "engineer", "architect", "programmer", "coder"];
283    let interests = [
284        "graph databases",
285        "distributed systems",
286        "machine learning",
287        "web development",
288        "mobile apps",
289    ];
290
291    format!(
292        "A {} {} interested in {}",
293        adjectives.choose(rng).unwrap(),
294        roles.choose(rng).unwrap(),
295        interests.choose(rng).unwrap()
296    )
297}