1use quantrs2_circuit::builder::{Circuit, Simulator};
7use quantrs2_core::{error::QuantRS2Result, qubit::QubitId};
8use scirs2_core::random::ChaCha8Rng;
9use scirs2_core::random::{Rng, SeedableRng};
10use std::time::{Duration, Instant};
11
12use crate::optimized_simulator::OptimizedSimulator;
13use crate::optimized_simulator_chunked::OptimizedSimulatorChunked;
14use crate::optimized_simulator_simple::OptimizedSimulatorSimple;
15use crate::statevector::StateVectorSimulator;
16
17#[derive(Debug, Clone)]
19pub struct BenchmarkResult {
20 pub simulator_name: String,
22 pub num_qubits: usize,
24 pub num_gates: usize,
26 pub duration: Duration,
28 pub notes: Option<String>,
30 pub peak_memory: Option<usize>,
32}
33
34impl BenchmarkResult {
35 #[must_use]
37 pub fn new(
38 simulator_name: &str,
39 num_qubits: usize,
40 num_gates: usize,
41 duration: Duration,
42 notes: Option<String>,
43 peak_memory: Option<usize>,
44 ) -> Self {
45 Self {
46 simulator_name: simulator_name.to_string(),
47 num_qubits,
48 num_gates,
49 duration,
50 notes,
51 peak_memory,
52 }
53 }
54
55 #[must_use]
57 pub fn format(&self) -> String {
58 let duration_ms = self.duration.as_millis();
59 let rate = if duration_ms > 0 {
60 self.num_gates as f64 / (duration_ms as f64 / 1000.0)
61 } else {
62 f64::INFINITY
63 };
64
65 let notes = if let Some(ref notes) = self.notes {
66 format!(" ({notes})")
67 } else {
68 String::new()
69 };
70
71 let memory_str = if let Some(mem) = self.peak_memory {
72 format!(", Memory: {:.2} MB", mem as f64 / (1024.0 * 1024.0))
73 } else {
74 String::new()
75 };
76
77 format!(
78 "Simulator: {}{}\n Qubits: {}\n Gates: {}\n Time: {} ms\n Rate: {:.2} gates/s{}",
79 self.simulator_name,
80 notes,
81 self.num_qubits,
82 self.num_gates,
83 duration_ms,
84 rate,
85 memory_str
86 )
87 }
88}
89
90pub fn run_benchmark<S, const N: usize>(
92 name: &str,
93 simulator: &S,
94 circuit: &Circuit<N>,
95 notes: Option<String>,
96) -> QuantRS2Result<BenchmarkResult>
97where
98 S: Simulator<N>,
99{
100 let num_qubits = N;
101 let num_gates = circuit.gates().len();
102
103 let start = Instant::now();
104 let _result = simulator.run(circuit)?;
105 let duration = start.elapsed();
106
107 Ok(BenchmarkResult::new(
108 name, num_qubits, num_gates, duration, notes, None,
109 ))
110}
111
112#[must_use]
114pub fn compare_simulators<const N: usize>(circuit: &Circuit<N>) -> Vec<BenchmarkResult> {
115 let mut results = Vec::new();
116
117 if N <= 24 {
119 let standard_sim = StateVectorSimulator::new();
121 if let Ok(result) = run_benchmark("Standard Simulator", &standard_sim, circuit, None) {
122 results.push(result);
123 }
124 }
125
126 let simple_opt_sim = OptimizedSimulatorSimple::new();
128 if let Ok(result) = run_benchmark("Simple Optimized", &simple_opt_sim, circuit, None) {
129 results.push(result);
130 }
131
132 let chunked_sim = OptimizedSimulatorChunked::new();
134 if let Ok(result) = run_benchmark("Chunked Optimized", &chunked_sim, circuit, None) {
135 results.push(result);
136 }
137
138 let optimized_sim = OptimizedSimulator::new();
140 if let Ok(result) = run_benchmark(
141 "Full Optimized",
142 &optimized_sim,
143 circuit,
144 Some("Auto-selection".to_string()),
145 ) {
146 results.push(result);
147 }
148
149 if N <= 20 {
151 let memory_efficient_sim = OptimizedSimulator::memory_efficient();
152 if let Ok(result) = run_benchmark(
153 "Memory Efficient",
154 &memory_efficient_sim,
155 circuit,
156 Some("Low memory".to_string()),
157 ) {
158 results.push(result);
159 }
160 }
161
162 results
163}
164
165#[must_use]
167pub fn generate_benchmark_circuit<const N: usize>(
168 num_gates: usize,
169 two_qubit_ratio: f64,
170) -> Circuit<N> {
171 let mut circuit = Circuit::new();
172 let mut rng = ChaCha8Rng::seed_from_u64(42); for _ in 0..num_gates {
175 let is_two_qubit = rng.random::<f64>() < two_qubit_ratio;
177
178 if is_two_qubit && N > 1 {
179 let qubit1 = QubitId::new(rng.random_range(0..N as u32));
181 let mut qubit2 = QubitId::new(rng.random_range(0..N as u32));
182 while qubit2 == qubit1 {
183 qubit2 = QubitId::new(rng.random_range(0..N as u32));
184 }
185
186 let gate_type = rng.random_range(0..3);
188 match gate_type {
189 0 => {
190 let _ = circuit.cnot(qubit1, qubit2);
191 }
192 1 => {
193 let _ = circuit.cz(qubit1, qubit2);
194 }
195 _ => {
196 let _ = circuit.swap(qubit1, qubit2);
197 }
198 }
199 } else {
200 let qubit = QubitId::new(rng.random_range(0..N as u32));
202
203 let gate_type = rng.random_range(0..7);
205 match gate_type {
206 0 => {
207 let _ = circuit.h(qubit);
208 }
209 1 => {
210 let _ = circuit.x(qubit);
211 }
212 2 => {
213 let _ = circuit.y(qubit);
214 }
215 3 => {
216 let _ = circuit.z(qubit);
217 }
218 4 => {
219 let _ = circuit.s(qubit);
220 }
221 5 => {
222 let _ = circuit.t(qubit);
223 }
224 _ => {
225 let angle = rng.random_range(0.0..std::f64::consts::TAU);
227 let rotation_type = rng.random_range(0..3);
228 match rotation_type {
229 0 => {
230 let _ = circuit.rx(qubit, angle);
231 }
232 1 => {
233 let _ = circuit.ry(qubit, angle);
234 }
235 _ => {
236 let _ = circuit.rz(qubit, angle);
237 }
238 }
239 }
240 }
241 }
242 }
243
244 circuit
245}
246
247pub fn run_benchmark_suite() {
249 println!("=== Quantrs Simulator Benchmark Suite ===");
250 println!("Testing various circuit sizes with different simulator implementations");
251 println!();
252
253 benchmark_circuit_size::<16>("Small", 16, 100, 0.3);
255
256 benchmark_circuit_size::<20>("Medium", 20, 50, 0.3);
258
259 benchmark_circuit_size::<25>("Large", 25, 20, 0.2);
261
262 benchmark_large_circuit::<28>("Very Large", 28, 10, 0.1);
264}
265
266fn benchmark_circuit_size<const N: usize>(
268 size_name: &str,
269 max_qubits: usize,
270 gates_per_qubit: usize,
271 two_qubit_ratio: f64,
272) {
273 println!("\n=== {size_name} Circuit Tests (up to {max_qubits} qubits) ===");
274
275 if N < max_qubits {
277 println!("Cannot benchmark this size - const generic N is too small");
278 return;
279 }
280
281 for qubits in [
283 max_qubits / 4,
284 max_qubits / 2,
285 (3 * max_qubits) / 4,
286 max_qubits,
287 ] {
288 let num_gates = qubits * gates_per_qubit;
289 println!("\nCircuit with {qubits} qubits and {num_gates} gates:");
290
291 let circuit = generate_benchmark_circuit::<N>(num_gates, two_qubit_ratio);
293
294 let results = compare_simulators(&circuit);
296
297 for result in &results {
299 println!("{}\n", result.format());
300 }
301 }
302}
303
304fn benchmark_large_circuit<const N: usize>(
306 size_name: &str,
307 max_qubits: usize,
308 gates_per_qubit: usize,
309 two_qubit_ratio: f64,
310) {
311 println!("\n=== {size_name} Circuit Tests ({max_qubits} qubits) ===");
312
313 if N < max_qubits {
315 println!("Cannot benchmark this size - const generic N is too small");
316 return;
317 }
318
319 let num_gates = max_qubits * gates_per_qubit;
320 println!("\nCircuit with {max_qubits} qubits and {num_gates} gates:");
321
322 let circuit = generate_benchmark_circuit::<N>(num_gates, two_qubit_ratio);
324
325 let mut results = Vec::new();
327
328 let chunked_sim = OptimizedSimulatorChunked::new();
330 if let Ok(result) = run_benchmark("Chunked Optimized", &chunked_sim, &circuit, None) {
331 results.push(result);
332 }
333
334 let optimized_sim = OptimizedSimulator::new();
336 if let Ok(result) = run_benchmark(
337 "Full Optimized",
338 &optimized_sim,
339 &circuit,
340 Some("Auto-selection".to_string()),
341 ) {
342 results.push(result);
343 }
344
345 for result in &results {
347 println!("{}\n", result.format());
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 #[ignore] fn benchmark_small_circuit() {
358 const QUBITS: usize = 16;
359 let circuit = generate_benchmark_circuit::<QUBITS>(100, 0.3);
360
361 let results = compare_simulators(&circuit);
362
363 for result in &results {
364 println!("{}\n", result.format());
365 }
366 }
367
368 #[test]
369 #[ignore] fn benchmark_medium_circuit() {
371 const QUBITS: usize = 20;
372 let circuit = generate_benchmark_circuit::<QUBITS>(50, 0.3);
373
374 let results = compare_simulators(&circuit);
375
376 for result in &results {
377 println!("{}\n", result.format());
378 }
379 }
380
381 #[test]
382 #[ignore] fn benchmark_large_circuit() {
384 const QUBITS: usize = 25;
385 let circuit = generate_benchmark_circuit::<QUBITS>(20, 0.2);
386
387 let mut results = Vec::new();
389
390 let chunked_sim = OptimizedSimulatorChunked::new();
392 if let Ok(result) = run_benchmark("Chunked Optimized", &chunked_sim, &circuit, None) {
393 results.push(result);
394 }
395
396 let optimized_sim = OptimizedSimulator::new();
397 if let Ok(result) = run_benchmark(
398 "Full Optimized",
399 &optimized_sim,
400 &circuit,
401 Some("Auto-selection".to_string()),
402 ) {
403 results.push(result);
404 }
405
406 for result in &results {
407 println!("{}\n", result.format());
408 }
409 }
410
411 #[test]
412 #[ignore] fn run_full_benchmark_suite() {
414 run_benchmark_suite();
415 }
416}