quantrs2_sim/cuquantum/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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);
221        // On non-macOS with cuquantum feature, simulation is not yet implemented
222        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
223        {
224            assert!(
225                result.is_err(),
226                "Expected error for unimplemented cuStateVec"
227            );
228        }
229        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
230        {
231            let result = result.expect("Should simulate circuit");
232            assert_eq!(result.num_qubits, 2);
233            assert!(result.state_vector.is_some());
234            let sv = result
235                .state_vector
236                .as_ref()
237                .expect("Should have state vector");
238            assert_eq!(sv.len(), 4);
239            assert!((sv[0].norm() - 1.0).abs() < 1e-10);
240        }
241    }
242    #[test]
243    fn test_custatevec_statistics() {
244        use quantrs2_circuit::prelude::Circuit;
245        let config = CuQuantumConfig::default();
246        let mut sim = CuStateVecSimulator::new(config).expect("Should create simulator");
247        let stats = sim.stats();
248        assert_eq!(stats.total_simulations, 0);
249        let circuit: Circuit<2> = Circuit::new();
250        let result = sim.simulate(&circuit);
251        let stats_after = sim.stats();
252        // On non-macOS with cuquantum feature, simulation fails so stats won't increment
253        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
254        {
255            assert!(result.is_err());
256            assert_eq!(stats_after.total_simulations, 0);
257        }
258        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
259        {
260            let _ = result;
261            assert_eq!(stats_after.total_simulations, 1);
262        }
263        sim.reset_stats();
264        let stats_reset = sim.stats();
265        assert_eq!(stats_reset.total_simulations, 0);
266    }
267    #[test]
268    fn test_unified_simulator_auto_selection() {
269        use quantrs2_circuit::prelude::Circuit;
270        let config_small = CuQuantumConfig::default();
271        let mut sim_small = CuQuantumSimulator::new(config_small).expect("Should create simulator");
272        let circuit: Circuit<2> = Circuit::new();
273        let result = sim_small.simulate(&circuit);
274        // On non-macOS with cuquantum feature, simulation is not yet implemented
275        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
276        {
277            assert!(
278                result.is_err(),
279                "Expected error for unimplemented cuQuantum"
280            );
281        }
282        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
283        {
284            let result = result.expect("Should simulate");
285            assert_eq!(result.num_qubits, 2);
286            let mut config_large = CuQuantumConfig::default();
287            config_large.max_statevec_qubits = 10;
288            let mut sim_large =
289                CuQuantumSimulator::new(config_large).expect("Should create simulator");
290            let result_large = sim_large.simulate(&circuit).expect("Should simulate");
291            assert_eq!(result_large.num_qubits, 2);
292        }
293    }
294    #[test]
295    fn test_cutensornet_network_building() {
296        use quantrs2_circuit::prelude::Circuit;
297        let config = CuQuantumConfig::default();
298        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
299        let circuit: Circuit<4> = Circuit::new();
300        sim.build_network(&circuit)
301            .expect("Should build tensor network");
302        assert!(sim.tensor_network.is_some());
303    }
304    #[test]
305    fn test_cutensornet_contraction() {
306        use quantrs2_circuit::prelude::Circuit;
307        let config = CuQuantumConfig::default();
308        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
309        let circuit: Circuit<3> = Circuit::new();
310        sim.build_network(&circuit)
311            .expect("Should build tensor network");
312        let result = sim.contract(&[0, 1, 2]);
313        // On non-macOS with cuquantum feature, contraction is not yet implemented
314        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
315        {
316            assert!(
317                result.is_err(),
318                "Expected error for unimplemented cuTensorNet"
319            );
320        }
321        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
322        {
323            let amplitudes = result.expect("Should contract network");
324            assert_eq!(amplitudes.len(), 8);
325        }
326    }
327    #[test]
328    fn test_cutensornet_expectation_value() {
329        use quantrs2_circuit::prelude::Circuit;
330        let config = CuQuantumConfig::default();
331        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
332        let circuit: Circuit<2> = Circuit::new();
333        sim.build_network(&circuit)
334            .expect("Should build tensor network");
335        let observable = Observable::PauliZ(vec![0]);
336        let result = sim.expectation_value(&observable);
337        // On non-macOS with cuquantum feature, expectation value is not yet implemented
338        #[cfg(all(feature = "cuquantum", not(target_os = "macos")))]
339        {
340            assert!(
341                result.is_err(),
342                "Expected error for unimplemented cuTensorNet"
343            );
344        }
345        #[cfg(any(target_os = "macos", not(feature = "cuquantum")))]
346        {
347            let exp_val = result.expect("Should compute expectation");
348            assert!((exp_val - 0.5).abs() < 1e-10);
349        }
350    }
351    #[test]
352    fn test_tensor_network_state_creation() {
353        use quantrs2_circuit::prelude::Circuit;
354        let circuit: Circuit<3> = Circuit::new();
355        let network = TensorNetworkState::from_circuit(&circuit).expect("Should create network");
356        assert!(
357            network.num_tensors() >= 3,
358            "Should have at least 3 tensors (one per qubit)"
359        );
360    }
361    #[test]
362    fn test_contraction_algorithms() {
363        use quantrs2_circuit::prelude::Circuit;
364        let config = CuQuantumConfig::default();
365        let mut sim = CuTensorNetSimulator::new(config).expect("Should create simulator");
366        let circuit: Circuit<4> = Circuit::new();
367        sim.build_network(&circuit)
368            .expect("Should build tensor network");
369        let path = sim
370            .find_contraction_order()
371            .expect("Should find contraction order");
372        assert!(!path.contractions.is_empty() || path.total_cost() >= 0.0);
373    }
374    #[test]
375    fn test_device_info_methods() {
376        let info = CudaDeviceInfo {
377            device_id: 0,
378            name: "Test Device".to_string(),
379            total_memory: 80 * 1024 * 1024 * 1024,
380            free_memory: 70 * 1024 * 1024 * 1024,
381            compute_capability: (8, 0),
382            sm_count: 108,
383            max_threads_per_block: 1024,
384            warp_size: 32,
385            has_tensor_cores: true,
386        };
387        let max_qubits = info.max_statevec_qubits();
388        assert!(
389            max_qubits >= 31,
390            "Expected >= 31 qubits for A100, got {}",
391            max_qubits
392        );
393    }
394    #[test]
395    fn test_is_available() {
396        let available = CuStateVecSimulator::is_available();
397        #[cfg(not(feature = "cuquantum"))]
398        assert!(!available);
399    }
400    #[test]
401    fn test_observable_variants() {
402        let obs_z = Observable::PauliZ(vec![0, 1]);
403        let obs_x = Observable::PauliX(vec![0]);
404        let obs_y = Observable::PauliY(vec![1]);
405        let mut hermitian = Array2::zeros((2, 2));
406        hermitian[[0, 0]] = Complex64::new(1.0, 0.0);
407        hermitian[[1, 1]] = Complex64::new(-1.0, 0.0);
408        let obs_h = Observable::Hermitian(hermitian);
409        let obs_sum = Observable::Sum(vec![
410            Observable::PauliZ(vec![0]),
411            Observable::PauliZ(vec![1]),
412        ]);
413        let obs_prod = Observable::Product(vec![
414            Observable::PauliX(vec![0]),
415            Observable::PauliY(vec![1]),
416        ]);
417        assert!(matches!(obs_z, Observable::PauliZ(_)));
418        assert!(matches!(obs_x, Observable::PauliX(_)));
419        assert!(matches!(obs_y, Observable::PauliY(_)));
420        assert!(matches!(obs_h, Observable::Hermitian(_)));
421        assert!(matches!(obs_sum, Observable::Sum(_)));
422        assert!(matches!(obs_prod, Observable::Product(_)));
423    }
424    #[test]
425    fn test_performance_estimator_creation() {
426        let config = CuQuantumConfig::default();
427        let estimator = PerformanceEstimator::with_default_device(config);
428        assert!(estimator.is_ok());
429    }
430    #[test]
431    fn test_performance_estimate_small_circuit() {
432        use quantrs2_circuit::prelude::Circuit;
433        let config = CuQuantumConfig::default();
434        let estimator =
435            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
436        let circuit: Circuit<5> = Circuit::new();
437        let estimate = estimator.estimate(&circuit);
438        assert!(estimate.fits_in_memory);
439        assert_eq!(
440            estimate.recommended_backend,
441            RecommendedBackend::StateVector
442        );
443        assert!(estimate.estimated_memory_bytes > 0);
444        assert!(estimate.estimated_gpu_utilization >= 0.0);
445        assert!(estimate.estimated_gpu_utilization <= 1.0);
446    }
447    #[test]
448    fn test_performance_estimate_memory_calculation() {
449        let config = CuQuantumConfig::default();
450        let estimator =
451            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
452        let device_info = estimator.device_info();
453        let _ = device_info;
454        use quantrs2_circuit::prelude::Circuit;
455        let circuit_10: Circuit<10> = Circuit::new();
456        let estimate_10 = estimator.estimate(&circuit_10);
457        assert_eq!(estimate_10.estimated_memory_bytes, 1024 * 16);
458        let circuit_20: Circuit<20> = Circuit::new();
459        let estimate_20 = estimator.estimate(&circuit_20);
460        assert_eq!(estimate_20.estimated_memory_bytes, 1024 * 1024 * 16);
461    }
462    #[test]
463    fn test_performance_estimate_flops_calculation() {
464        use quantrs2_circuit::prelude::Circuit;
465        let config = CuQuantumConfig::default();
466        let estimator =
467            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
468        let circuit_empty: Circuit<5> = Circuit::new();
469        let estimate_empty = estimator.estimate(&circuit_empty);
470        assert_eq!(estimate_empty.estimated_flops, 0.0);
471    }
472    #[test]
473    fn test_circuit_complexity_analysis() {
474        use quantrs2_circuit::prelude::Circuit;
475        let circuit: Circuit<4> = Circuit::new();
476        let complexity = CircuitComplexity::analyze(&circuit);
477        assert_eq!(complexity.num_qubits, 4);
478        assert_eq!(complexity.num_gates, 0);
479        assert_eq!(complexity.depth, 0);
480        assert_eq!(complexity.entanglement_degree, 0.0);
481    }
482    #[test]
483    fn test_gpu_resource_planner() {
484        use quantrs2_circuit::prelude::Circuit;
485        let device = CudaDeviceInfo {
486            device_id: 0,
487            name: "Test GPU".to_string(),
488            total_memory: 16 * 1024 * 1024 * 1024,
489            free_memory: 12 * 1024 * 1024 * 1024,
490            compute_capability: (8, 6),
491            sm_count: 68,
492            max_threads_per_block: 1024,
493            warp_size: 32,
494            has_tensor_cores: true,
495        };
496        let config = CuQuantumConfig::default();
497        let planner = GpuResourcePlanner::new(vec![device], config);
498        let circuits: Vec<Circuit<4>> = vec![Circuit::new(), Circuit::new(), Circuit::new()];
499        let assignments = planner.plan_batch(&circuits);
500        assert_eq!(assignments.len(), 3);
501        for (device_id, _) in &assignments {
502            assert_eq!(*device_id, 0);
503        }
504        let batch_memory = planner.estimate_batch_memory(&circuits);
505        assert_eq!(batch_memory, 3 * 16 * 16);
506    }
507    #[test]
508    fn test_recommended_backend_enum() {
509        assert_ne!(
510            RecommendedBackend::StateVector,
511            RecommendedBackend::TensorNetwork
512        );
513        assert_ne!(RecommendedBackend::Hybrid, RecommendedBackend::NotFeasible);
514        let sv = format!("{:?}", RecommendedBackend::StateVector);
515        assert!(sv.contains("StateVector"));
516    }
517    #[test]
518    fn test_performance_suggestions() {
519        use quantrs2_circuit::prelude::Circuit;
520        let mut config = CuQuantumConfig::default();
521        config.gate_fusion_level = GateFusionLevel::None;
522        let estimator =
523            PerformanceEstimator::with_default_device(config).expect("Should create estimator");
524        let circuit: Circuit<26> = Circuit::new();
525        let estimate = estimator.estimate(&circuit);
526        let has_fusion_suggestion = estimate
527            .suggestions
528            .iter()
529            .any(|s| s.contains("gate fusion"));
530        assert!(
531            has_fusion_suggestion,
532            "Should suggest gate fusion for 26 qubit circuit"
533        );
534    }
535    #[test]
536    fn test_multi_gpu_planner() {
537        use quantrs2_circuit::prelude::Circuit;
538        let devices = vec![
539            CudaDeviceInfo {
540                device_id: 0,
541                name: "GPU 0".to_string(),
542                total_memory: 16 * 1024 * 1024 * 1024,
543                free_memory: 12 * 1024 * 1024 * 1024,
544                compute_capability: (8, 6),
545                sm_count: 68,
546                max_threads_per_block: 1024,
547                warp_size: 32,
548                has_tensor_cores: true,
549            },
550            CudaDeviceInfo {
551                device_id: 1,
552                name: "GPU 1".to_string(),
553                total_memory: 16 * 1024 * 1024 * 1024,
554                free_memory: 12 * 1024 * 1024 * 1024,
555                compute_capability: (8, 6),
556                sm_count: 68,
557                max_threads_per_block: 1024,
558                warp_size: 32,
559                has_tensor_cores: true,
560            },
561        ];
562        let config = CuQuantumConfig::default();
563        let planner = GpuResourcePlanner::new(devices, config);
564        let circuits: Vec<Circuit<4>> = vec![
565            Circuit::new(),
566            Circuit::new(),
567            Circuit::new(),
568            Circuit::new(),
569        ];
570        let assignments = planner.plan_batch(&circuits);
571        assert_eq!(assignments.len(), 4);
572        assert_eq!(assignments[0].0, 0);
573        assert_eq!(assignments[1].0, 1);
574        assert_eq!(assignments[2].0, 0);
575        assert_eq!(assignments[3].0, 1);
576    }
577}