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