1use crate::error::{Result, SimulatorError};
6use quantrs2_circuit::prelude::Circuit;
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::Complex64;
9use std::collections::HashMap;
10
11use super::types::{
12 CircuitComplexity, ComputePrecision, ContractionPath, CuQuantumConfig, CuQuantumError,
13 CuQuantumResult, CuQuantumSimulator, CuStateVecSimulator, CuTensorNetSimulator, CudaDeviceInfo,
14 GateFusionLevel, GpuResourcePlanner, Observable, PerformanceEstimator, RecommendedBackend,
15 SimulationStats, TensorNetworkState,
16};
17
18impl From<CuQuantumError> for SimulatorError {
19 fn from(err: CuQuantumError) -> Self {
20 SimulatorError::GpuError(err.to_string())
21 }
22}
23#[cfg(test)]
24#[allow(clippy::field_reassign_with_default)]
25mod tests {
26 use super::*;
27 #[test]
28 fn test_config_default() {
29 let config = CuQuantumConfig::default();
30 assert_eq!(config.device_id, -1);
31 assert!(!config.multi_gpu);
32 assert_eq!(config.precision, ComputePrecision::Double);
33 }
34 #[test]
35 fn test_config_large_circuit() {
36 let config = CuQuantumConfig::large_circuit();
37 assert!(config.memory_optimization);
38 assert_eq!(config.gate_fusion_level, GateFusionLevel::Aggressive);
39 }
40 #[test]
41 fn test_config_variational() {
42 let config = CuQuantumConfig::variational();
43 assert!(config.async_execution);
44 assert_eq!(config.gate_fusion_level, GateFusionLevel::Moderate);
45 }
46 #[test]
47 fn test_config_multi_gpu() {
48 let config = CuQuantumConfig::multi_gpu(4);
49 assert!(config.multi_gpu);
50 assert_eq!(config.num_gpus, 4);
51 }
52 #[test]
53 fn test_device_info_mock() {
54 let info = CuStateVecSimulator::get_device_info(-1).expect("Should get mock device info");
55 assert!(info.total_memory > 0);
56 assert!(info.compute_capability.0 >= 7);
57 }
58 #[test]
59 fn test_device_max_qubits() {
60 let info = CudaDeviceInfo {
61 device_id: 0,
62 name: "Test A100".to_string(),
63 total_memory: 40 * 1024 * 1024 * 1024,
64 free_memory: 32 * 1024 * 1024 * 1024,
65 compute_capability: (8, 0),
66 sm_count: 108,
67 max_threads_per_block: 1024,
68 warp_size: 32,
69 has_tensor_cores: true,
70 };
71 let max_qubits = info.max_statevec_qubits();
72 assert!(
73 max_qubits >= 30,
74 "Expected >= 30 qubits, got {}",
75 max_qubits
76 );
77 assert!(
78 max_qubits <= 35,
79 "Expected <= 35 qubits, got {}",
80 max_qubits
81 );
82 let info_small = CudaDeviceInfo {
83 device_id: 0,
84 name: "Test RTX 3080".to_string(),
85 total_memory: 12 * 1024 * 1024 * 1024,
86 free_memory: 10 * 1024 * 1024 * 1024,
87 compute_capability: (8, 6),
88 sm_count: 68,
89 max_threads_per_block: 1024,
90 warp_size: 32,
91 has_tensor_cores: true,
92 };
93 let max_qubits_small = info_small.max_statevec_qubits();
94 assert!(
95 max_qubits_small >= 28,
96 "Expected >= 28 qubits for 12GB GPU, got {}",
97 max_qubits_small
98 );
99 }
100 #[test]
101 fn test_custatevec_simulator_creation() {
102 let config = CuQuantumConfig::default();
103 let sim = CuStateVecSimulator::new(config);
104 assert!(sim.is_ok());
105 }
106 #[test]
107 fn test_cutensornet_simulator_creation() {
108 let config = CuQuantumConfig::default();
109 let sim = CuTensorNetSimulator::new(config);
110 assert!(sim.is_ok());
111 }
112 #[test]
113 fn test_simulation_stats() {
114 let mut stats = SimulationStats::default();
115 stats.total_simulations = 10;
116 stats.total_gates = 100;
117 stats.total_time_ms = 500.0;
118 stats.total_flops = 1e9;
119 assert_eq!(stats.avg_gates_per_sim(), 10.0);
120 assert_eq!(stats.avg_time_per_sim(), 50.0);
121 assert!((stats.throughput_gflops() - 2.0).abs() < 0.01);
122 }
123 #[test]
124 fn test_contraction_path() {
125 let mut path = ContractionPath::new();
126 path.add_contraction(0, 1);
127 path.add_contraction(0, 2);
128 assert_eq!(path.contractions.len(), 2);
129 assert!(path.total_cost() > 0.0);
130 }
131 #[test]
132 fn test_unified_simulator_creation() {
133 let config = CuQuantumConfig::default();
134 let sim = CuQuantumSimulator::new(config);
135 assert!(sim.is_ok());
136 }
137 #[test]
138 fn test_observable_creation() {
139 let obs = Observable::PauliZ(vec![0, 1]);
140 match obs {
141 Observable::PauliZ(qubits) => assert_eq!(qubits.len(), 2),
142 _ => panic!("Wrong observable type"),
143 }
144 }
145 #[test]
146 fn test_cuquantum_result_from_state_vector() {
147 use scirs2_core::ndarray::Array1;
148 use scirs2_core::Complex64;
149 let mut state = Array1::zeros(4);
150 state[0] = Complex64::new(1.0, 0.0);
151 let result = CuQuantumResult::from_state_vector(state.clone(), 2);
152 assert_eq!(result.num_qubits, 2);
153 assert!(result.state_vector.is_some());
154 assert!(result.counts.is_empty());
155 let probs = result.probabilities().expect("Should have probabilities");
156 assert_eq!(probs.len(), 4);
157 assert!((probs[0] - 1.0).abs() < 1e-10);
158 assert!(probs[1] < 1e-10);
159 assert!(probs[2] < 1e-10);
160 assert!(probs[3] < 1e-10);
161 }
162 #[test]
163 fn test_cuquantum_result_from_counts() {
164 let mut counts = HashMap::new();
165 counts.insert("00".to_string(), 500);
166 counts.insert("11".to_string(), 500);
167 let result = CuQuantumResult::from_counts(counts.clone(), 2);
168 assert_eq!(result.num_qubits, 2);
169 assert!(result.state_vector.is_none());
170 assert_eq!(result.counts.len(), 2);
171 assert_eq!(*result.counts.get("00").unwrap_or(&0), 500);
172 assert_eq!(*result.counts.get("11").unwrap_or(&0), 500);
173 }
174 #[test]
175 fn test_cuquantum_result_expectation_z() {
176 use scirs2_core::ndarray::Array1;
177 use scirs2_core::Complex64;
178 let mut state_zero = Array1::zeros(2);
179 state_zero[0] = Complex64::new(1.0, 0.0);
180 let result_zero = CuQuantumResult::from_state_vector(state_zero, 1);
181 let exp_z = result_zero
182 .expectation_z(0)
183 .expect("Should compute expectation");
184 assert!(
185 (exp_z - 1.0).abs() < 1e-10,
186 "Expected +1 for |0⟩, got {}",
187 exp_z
188 );
189 let mut state_one = Array1::zeros(2);
190 state_one[1] = Complex64::new(1.0, 0.0);
191 let result_one = CuQuantumResult::from_state_vector(state_one, 1);
192 let exp_z_one = result_one
193 .expectation_z(0)
194 .expect("Should compute expectation");
195 assert!(
196 (exp_z_one - (-1.0)).abs() < 1e-10,
197 "Expected -1 for |1⟩, got {}",
198 exp_z_one
199 );
200 let mut state_plus = Array1::zeros(2);
201 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
202 state_plus[0] = Complex64::new(inv_sqrt2, 0.0);
203 state_plus[1] = Complex64::new(inv_sqrt2, 0.0);
204 let result_plus = CuQuantumResult::from_state_vector(state_plus, 1);
205 let exp_z_plus = result_plus
206 .expectation_z(0)
207 .expect("Should compute expectation");
208 assert!(
209 exp_z_plus.abs() < 1e-10,
210 "Expected 0 for |+⟩, got {}",
211 exp_z_plus
212 );
213 }
214 #[test]
215 fn test_custatevec_circuit_simulation() {
216 use quantrs2_circuit::prelude::Circuit;
217 let config = CuQuantumConfig::default();
218 let mut sim = CuStateVecSimulator::new(config).expect("Should create simulator");
219 let circuit: Circuit<2> = Circuit::new();
220 let result = sim.simulate(&circuit).expect("Should simulate circuit");
224 assert_eq!(result.num_qubits, 2);
225 assert!(result.state_vector.is_some());
226 let sv = result
227 .state_vector
228 .as_ref()
229 .expect("Should have state vector");
230 assert_eq!(sv.len(), 4);
231 assert!((sv[0].norm() - 1.0).abs() < 1e-10);
232 }
233 #[test]
234 fn test_custatevec_statistics() {
235 use quantrs2_circuit::prelude::Circuit;
236 let config = CuQuantumConfig::default();
237 let mut sim = CuStateVecSimulator::new(config).expect("Should create simulator");
238 let stats = sim.stats();
239 assert_eq!(stats.total_simulations, 0);
240 let circuit: Circuit<2> = Circuit::new();
241 sim.simulate(&circuit).expect("Should simulate circuit");
242 let stats_after = sim.stats();
243 assert_eq!(stats_after.total_simulations, 1);
244 sim.reset_stats();
245 let stats_reset = sim.stats();
246 assert_eq!(stats_reset.total_simulations, 0);
247 }
248 #[test]
249 fn test_unified_simulator_auto_selection() {
250 use quantrs2_circuit::prelude::Circuit;
251 let config_small = CuQuantumConfig::default();
252 let mut sim_small = CuQuantumSimulator::new(config_small).expect("Should create simulator");
253 let circuit: Circuit<2> = Circuit::new();
254 let result = sim_small.simulate(&circuit).expect("Should simulate");
255 assert_eq!(result.num_qubits, 2);
256
257 let mut config_large = CuQuantumConfig::default();
258 config_large.max_statevec_qubits = 10;
259 let mut sim_large = CuQuantumSimulator::new(config_large).expect("Should create simulator");
260 let result_large = sim_large.simulate(&circuit).expect("Should simulate");
261 assert_eq!(result_large.num_qubits, 2);
262 }
263 #[test]
264 fn test_cutensornet_network_building() {
265 use quantrs2_circuit::prelude::Circuit;
266 let config = CuQuantumConfig::default();
267 let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
268 let circuit: Circuit<4> = Circuit::new();
269 sim.build_network(&circuit)
270 .expect("Should build tensor network");
271 assert!(sim.tensor_network.is_some());
272 }
273 #[test]
274 fn test_cutensornet_contraction() {
275 use quantrs2_circuit::prelude::Circuit;
276 let config = CuQuantumConfig::default();
277 let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
278 let circuit: Circuit<3> = Circuit::new();
279 sim.build_network(&circuit)
280 .expect("Should build tensor network");
281 let result = sim.contract(&[0, 1, 2]);
282 #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
284 {
285 assert!(
286 result.is_err(),
287 "Expected error for unimplemented cuTensorNet"
288 );
289 }
290 #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
291 {
292 let amplitudes = result.expect("Should contract network");
293 assert_eq!(amplitudes.len(), 8);
294 }
295 }
296 #[test]
297 fn test_cutensornet_expectation_value() {
298 use quantrs2_circuit::prelude::Circuit;
299 let config = CuQuantumConfig::default();
300 let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
301 let circuit: Circuit<2> = Circuit::new();
302 sim.build_network(&circuit)
303 .expect("Should build tensor network");
304 let observable = Observable::PauliZ(vec![0]);
305 let result = sim.expectation_value(&observable);
306 #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
308 {
309 assert!(
310 result.is_err(),
311 "Expected error for unimplemented cuTensorNet"
312 );
313 }
314 #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
315 {
316 let exp_val = result.expect("Should compute expectation");
317 assert!((exp_val - 0.5).abs() < 1e-10);
318 }
319 }
320 #[test]
321 fn test_tensor_network_state_creation() {
322 use quantrs2_circuit::prelude::Circuit;
323 let circuit: Circuit<3> = Circuit::new();
324 let network = TensorNetworkState::from_circuit(&circuit).expect("Should create network");
325 assert!(
326 network.num_tensors() >= 3,
327 "Should have at least 3 tensors (one per qubit)"
328 );
329 }
330 #[test]
331 fn test_contraction_algorithms() {
332 use quantrs2_circuit::prelude::Circuit;
333 let config = CuQuantumConfig::default();
334 let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
335 let circuit: Circuit<4> = Circuit::new();
336 sim.build_network(&circuit)
337 .expect("Should build tensor network");
338 let path = sim
339 .find_contraction_order()
340 .expect("Should find contraction order");
341 assert!(!path.contractions.is_empty() || path.total_cost() >= 0.0);
342 }
343 #[test]
344 fn test_device_info_methods() {
345 let info = CudaDeviceInfo {
346 device_id: 0,
347 name: "Test Device".to_string(),
348 total_memory: 80 * 1024 * 1024 * 1024,
349 free_memory: 70 * 1024 * 1024 * 1024,
350 compute_capability: (8, 0),
351 sm_count: 108,
352 max_threads_per_block: 1024,
353 warp_size: 32,
354 has_tensor_cores: true,
355 };
356 let max_qubits = info.max_statevec_qubits();
357 assert!(
358 max_qubits >= 31,
359 "Expected >= 31 qubits for A100, got {}",
360 max_qubits
361 );
362 }
363 #[test]
364 fn test_is_available() {
365 let available = CuStateVecSimulator::is_available();
366 #[cfg(not(feature = "cuquantum"))]
367 assert!(!available);
368 }
369 #[test]
370 fn test_observable_variants() {
371 let obs_z = Observable::PauliZ(vec![0, 1]);
372 let obs_x = Observable::PauliX(vec![0]);
373 let obs_y = Observable::PauliY(vec![1]);
374 let mut hermitian = Array2::zeros((2, 2));
375 hermitian[[0, 0]] = Complex64::new(1.0, 0.0);
376 hermitian[[1, 1]] = Complex64::new(-1.0, 0.0);
377 let obs_h = Observable::Hermitian(hermitian);
378 let obs_sum = Observable::Sum(vec![
379 Observable::PauliZ(vec![0]),
380 Observable::PauliZ(vec![1]),
381 ]);
382 let obs_prod = Observable::Product(vec![
383 Observable::PauliX(vec![0]),
384 Observable::PauliY(vec![1]),
385 ]);
386 assert!(matches!(obs_z, Observable::PauliZ(_)));
387 assert!(matches!(obs_x, Observable::PauliX(_)));
388 assert!(matches!(obs_y, Observable::PauliY(_)));
389 assert!(matches!(obs_h, Observable::Hermitian(_)));
390 assert!(matches!(obs_sum, Observable::Sum(_)));
391 assert!(matches!(obs_prod, Observable::Product(_)));
392 }
393 #[test]
394 fn test_performance_estimator_creation() {
395 let config = CuQuantumConfig::default();
396 let estimator = PerformanceEstimator::with_default_device(config);
397 assert!(estimator.is_ok());
398 }
399 #[test]
400 fn test_performance_estimate_small_circuit() {
401 use quantrs2_circuit::prelude::Circuit;
402 let config = CuQuantumConfig::default();
403 let estimator =
404 PerformanceEstimator::with_default_device(config).expect("Should create estimator");
405 let circuit: Circuit<5> = Circuit::new();
406 let estimate = estimator.estimate(&circuit);
407 assert!(estimate.fits_in_memory);
408 assert_eq!(
409 estimate.recommended_backend,
410 RecommendedBackend::StateVector
411 );
412 assert!(estimate.estimated_memory_bytes > 0);
413 assert!(estimate.estimated_gpu_utilization >= 0.0);
414 assert!(estimate.estimated_gpu_utilization <= 1.0);
415 }
416 #[test]
417 fn test_performance_estimate_memory_calculation() {
418 let config = CuQuantumConfig::default();
419 let estimator =
420 PerformanceEstimator::with_default_device(config).expect("Should create estimator");
421 let device_info = estimator.device_info();
422 let _ = device_info;
423 use quantrs2_circuit::prelude::Circuit;
424 let circuit_10: Circuit<10> = Circuit::new();
425 let estimate_10 = estimator.estimate(&circuit_10);
426 assert_eq!(estimate_10.estimated_memory_bytes, 1024 * 16);
427 let circuit_20: Circuit<20> = Circuit::new();
428 let estimate_20 = estimator.estimate(&circuit_20);
429 assert_eq!(estimate_20.estimated_memory_bytes, 1024 * 1024 * 16);
430 }
431 #[test]
432 fn test_performance_estimate_flops_calculation() {
433 use quantrs2_circuit::prelude::Circuit;
434 let config = CuQuantumConfig::default();
435 let estimator =
436 PerformanceEstimator::with_default_device(config).expect("Should create estimator");
437 let circuit_empty: Circuit<5> = Circuit::new();
438 let estimate_empty = estimator.estimate(&circuit_empty);
439 assert_eq!(estimate_empty.estimated_flops, 0.0);
440 }
441 #[test]
442 fn test_circuit_complexity_analysis() {
443 use quantrs2_circuit::prelude::Circuit;
444 let circuit: Circuit<4> = Circuit::new();
445 let complexity = CircuitComplexity::analyze(&circuit);
446 assert_eq!(complexity.num_qubits, 4);
447 assert_eq!(complexity.num_gates, 0);
448 assert_eq!(complexity.depth, 0);
449 assert_eq!(complexity.entanglement_degree, 0.0);
450 }
451 #[test]
452 fn test_gpu_resource_planner() {
453 use quantrs2_circuit::prelude::Circuit;
454 let device = CudaDeviceInfo {
455 device_id: 0,
456 name: "Test GPU".to_string(),
457 total_memory: 16 * 1024 * 1024 * 1024,
458 free_memory: 12 * 1024 * 1024 * 1024,
459 compute_capability: (8, 6),
460 sm_count: 68,
461 max_threads_per_block: 1024,
462 warp_size: 32,
463 has_tensor_cores: true,
464 };
465 let config = CuQuantumConfig::default();
466 let planner = GpuResourcePlanner::new(vec![device], config);
467 let circuits: Vec<Circuit<4>> = vec![Circuit::new(), Circuit::new(), Circuit::new()];
468 let assignments = planner.plan_batch(&circuits);
469 assert_eq!(assignments.len(), 3);
470 for (device_id, _) in &assignments {
471 assert_eq!(*device_id, 0);
472 }
473 let batch_memory = planner.estimate_batch_memory(&circuits);
474 assert_eq!(batch_memory, 3 * 16 * 16);
475 }
476 #[test]
477 fn test_recommended_backend_enum() {
478 assert_ne!(
479 RecommendedBackend::StateVector,
480 RecommendedBackend::TensorNetwork
481 );
482 assert_ne!(RecommendedBackend::Hybrid, RecommendedBackend::NotFeasible);
483 let sv = format!("{:?}", RecommendedBackend::StateVector);
484 assert!(sv.contains("StateVector"));
485 }
486 #[test]
487 fn test_performance_suggestions() {
488 use quantrs2_circuit::prelude::Circuit;
489 let mut config = CuQuantumConfig::default();
490 config.gate_fusion_level = GateFusionLevel::None;
491 let estimator =
492 PerformanceEstimator::with_default_device(config).expect("Should create estimator");
493 let circuit: Circuit<26> = Circuit::new();
494 let estimate = estimator.estimate(&circuit);
495 let has_fusion_suggestion = estimate
496 .suggestions
497 .iter()
498 .any(|s| s.contains("gate fusion"));
499 assert!(
500 has_fusion_suggestion,
501 "Should suggest gate fusion for 26 qubit circuit"
502 );
503 }
504 #[test]
505 fn test_multi_gpu_planner() {
506 use quantrs2_circuit::prelude::Circuit;
507 let devices = vec![
508 CudaDeviceInfo {
509 device_id: 0,
510 name: "GPU 0".to_string(),
511 total_memory: 16 * 1024 * 1024 * 1024,
512 free_memory: 12 * 1024 * 1024 * 1024,
513 compute_capability: (8, 6),
514 sm_count: 68,
515 max_threads_per_block: 1024,
516 warp_size: 32,
517 has_tensor_cores: true,
518 },
519 CudaDeviceInfo {
520 device_id: 1,
521 name: "GPU 1".to_string(),
522 total_memory: 16 * 1024 * 1024 * 1024,
523 free_memory: 12 * 1024 * 1024 * 1024,
524 compute_capability: (8, 6),
525 sm_count: 68,
526 max_threads_per_block: 1024,
527 warp_size: 32,
528 has_tensor_cores: true,
529 },
530 ];
531 let config = CuQuantumConfig::default();
532 let planner = GpuResourcePlanner::new(devices, config);
533 let circuits: Vec<Circuit<4>> = vec![
534 Circuit::new(),
535 Circuit::new(),
536 Circuit::new(),
537 Circuit::new(),
538 ];
539 let assignments = planner.plan_batch(&circuits);
540 assert_eq!(assignments.len(), 4);
541 assert_eq!(assignments[0].0, 0);
542 assert_eq!(assignments[1].0, 1);
543 assert_eq!(assignments[2].0, 0);
544 assert_eq!(assignments[3].0, 1);
545 }
546}